diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml
index 6277e18c..db5fd66c 100644
--- a/StabilityMatrix.Avalonia/App.axaml
+++ b/StabilityMatrix.Avalonia/App.axaml
@@ -3,7 +3,6 @@
x:Class="StabilityMatrix.Avalonia.App"
xmlns:local="using:StabilityMatrix.Avalonia"
xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia"
-
RequestedThemeVariant="Default">
@@ -23,6 +22,7 @@
+
@@ -32,5 +32,8 @@
+
+
+
diff --git a/StabilityMatrix.Avalonia/Controls/SamplerCard.axaml b/StabilityMatrix.Avalonia/Controls/SamplerCard.axaml
index ea790c44..6503cff0 100644
--- a/StabilityMatrix.Avalonia/Controls/SamplerCard.axaml
+++ b/StabilityMatrix.Avalonia/Controls/SamplerCard.axaml
@@ -77,7 +77,7 @@
-
+
+ ItemsSource="{Binding Cards}">
diff --git a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml
index edf04f5c..d7bdbf88 100644
--- a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml
+++ b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml
@@ -38,7 +38,7 @@
+ ItemsSource="{Binding Cards}">
diff --git a/StabilityMatrix.Avalonia/Controls/UpscalerCard.axaml b/StabilityMatrix.Avalonia/Controls/UpscalerCard.axaml
index 7cffaafc..7fdb1e2d 100644
--- a/StabilityMatrix.Avalonia/Controls/UpscalerCard.axaml
+++ b/StabilityMatrix.Avalonia/Controls/UpscalerCard.axaml
@@ -18,7 +18,7 @@
-
+
DialogFactory.Get(vm =>
{
- vm.AddCards(new ViewModelBase[]
+ vm.AddCards(new LoadableViewModelBase[]
{
SamplerCardViewModel,
SeedCardViewModel,
@@ -413,7 +415,7 @@ public static class DesignData
DialogFactory.Get(vm =>
{
vm.Title = "Hires Fix";
- vm.AddCards(new ViewModelBase[]
+ vm.AddCards(new LoadableViewModelBase[]
{
SamplerCardViewModel,
SeedCardViewModel,
diff --git a/StabilityMatrix.Avalonia/Models/IJsonLoadableState.cs b/StabilityMatrix.Avalonia/Models/IJsonLoadableState.cs
new file mode 100644
index 00000000..95d4860d
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Models/IJsonLoadableState.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Nodes;
+
+namespace StabilityMatrix.Avalonia.Models;
+
+public interface IJsonLoadableState
+{
+ void LoadStateFromJsonObject(JsonObject state);
+
+ JsonObject SaveStateToJsonObject();
+}
diff --git a/StabilityMatrix.Avalonia/Models/ILoadableState.cs b/StabilityMatrix.Avalonia/Models/ILoadableState.cs
index c31401f8..cce10463 100644
--- a/StabilityMatrix.Avalonia/Models/ILoadableState.cs
+++ b/StabilityMatrix.Avalonia/Models/ILoadableState.cs
@@ -4,16 +4,23 @@ using System.Text.Json.Nodes;
namespace StabilityMatrix.Avalonia.Models;
-public interface ILoadableState
+public interface ILoadableState : IJsonLoadableState
{
- public Type LoadableStateType => typeof(T);
+ new Type LoadableStateType => typeof(T);
- public void LoadState(T state);
+ void LoadState(T state);
- public void LoadStateFromJsonObject(JsonObject state)
+ new void LoadStateFromJsonObject(JsonObject state)
{
state.Deserialize(LoadableStateType);
}
- public T SaveState();
+ T SaveState();
+
+ new JsonObject SaveStateToJsonObject()
+ {
+ var node = JsonSerializer.SerializeToNode(SaveState());
+ return node?.AsObject() ?? throw new
+ InvalidOperationException("Failed to serialize state to JSON object.");
+ }
}
diff --git a/StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs b/StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs
index df9ddaa2..5ad6717b 100644
--- a/StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs
+++ b/StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs
@@ -1,14 +1,13 @@
-using System.Text.Json.Serialization;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
namespace StabilityMatrix.Avalonia.Models.Inference;
[JsonSerializable(typeof(InferenceTextToImageModel))]
public class InferenceTextToImageModel
{
- public string? Prompt { get; init; }
- public string? NegativePrompt { get; init; }
public string? SelectedModelName { get; init; }
- public SeedCardModel? SeedCardState { get; init; }
- public SamplerCardModel? SamplerCardState { get; init; }
- public PromptCardModel? PromptCardState { get; init; }
+ public JsonObject? SeedCardState { get; init; }
+ public JsonObject? PromptCardState { get; init; }
+ public JsonObject? StackCardState { get; init; }
}
diff --git a/StabilityMatrix.Avalonia/Models/Inference/StackCardModel.cs b/StabilityMatrix.Avalonia/Models/Inference/StackCardModel.cs
new file mode 100644
index 00000000..295adb54
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Models/Inference/StackCardModel.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+
+namespace StabilityMatrix.Avalonia.Models.Inference;
+
+[JsonSerializable(typeof(StackCardModel))]
+public class StackCardModel
+{
+ public List? Cards { get; init; }
+}
diff --git a/StabilityMatrix.Avalonia/Models/Inference/StackExpanderModel.cs b/StabilityMatrix.Avalonia/Models/Inference/StackExpanderModel.cs
new file mode 100644
index 00000000..5c782de4
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Models/Inference/StackExpanderModel.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+using StabilityMatrix.Avalonia.ViewModels.Inference;
+
+namespace StabilityMatrix.Avalonia.Models.Inference;
+
+[JsonSerializable(typeof(StackExpanderModel))]
+public class StackExpanderModel : StackCardModel
+{
+ public string? Title { get; set; }
+ public bool IsEnabled { get; set; }
+}
diff --git a/StabilityMatrix.Avalonia/Models/Inference/UpscalerCardModel.cs b/StabilityMatrix.Avalonia/Models/Inference/UpscalerCardModel.cs
new file mode 100644
index 00000000..b454ca02
--- /dev/null
+++ b/StabilityMatrix.Avalonia/Models/Inference/UpscalerCardModel.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+using StabilityMatrix.Core.Models.Api.Comfy;
+
+namespace StabilityMatrix.Avalonia.Models.Inference;
+
+[JsonSerializable(typeof(UpscalerCardModel))]
+public class UpscalerCardModel
+{
+ public double Scale { get; init; } = 1;
+ public ComfyUpscaler? SelectedUpscaler { get; init; }
+}
diff --git a/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs b/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs
index 13eb0805..c06ac781 100644
--- a/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs
+++ b/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs
@@ -26,23 +26,19 @@ public class InferenceProjectDocument
public JsonObject? State { get; set; }
- public static InferenceProjectDocument FromLoadable(object loadableModel)
+ public static InferenceProjectDocument FromLoadable(IJsonLoadableState loadableModel)
{
- var document = new InferenceProjectDocument();
-
- if (loadableModel is InferenceTextToImageViewModel model)
- {
- document.ProjectType = InferenceProjectType.TextToImage;
- document.State = JsonSerializer.SerializeToNode(model.SaveState(), SerializerOptions)?.AsObject();
- }
- else
+ return new InferenceProjectDocument
{
- throw new InvalidOperationException(
- $"Unknown loadable model type: {loadableModel.GetType()}"
- );
- }
-
- return document;
+ ProjectType = loadableModel switch
+ {
+ InferenceTextToImageViewModel => InferenceProjectType.TextToImage,
+ _ => throw new InvalidOperationException(
+ $"Unknown loadable model type: {loadableModel.GetType()}"
+ )
+ },
+ State = loadableModel.SaveStateToJsonObject()
+ };
}
public Type GetViewModelType()
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs
index 553a7ddc..a1c17c64 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs
@@ -4,6 +4,7 @@ using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Media.Imaging;
@@ -24,9 +25,7 @@ using StabilityMatrix.Core.Models.Api.Comfy.WebSocketData;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(InferenceTextToImageView))]
-public partial class InferenceTextToImageViewModel
- : ViewModelBase,
- ILoadableState
+public partial class InferenceTextToImageViewModel : LoadableViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@@ -36,11 +35,10 @@ public partial class InferenceTextToImageViewModel
public IInferenceClientManager ClientManager { get; }
public SeedCardViewModel SeedCardViewModel { get; }
- public SamplerCardViewModel SamplerCardViewModel { get; }
- public SamplerCardViewModel HiresFixSamplerCardViewModel { get; }
+
public ImageGalleryCardViewModel ImageGalleryCardViewModel { get; }
public PromptCardViewModel PromptCardViewModel { get; }
- public StackCardViewModel ConfigCardViewModel { get; }
+ public StackCardViewModel StackCardViewModel { get; }
[ObservableProperty]
private string? selectedModelName;
@@ -70,36 +68,41 @@ public partial class InferenceTextToImageViewModel
SeedCardViewModel = vmFactory.Get();
SeedCardViewModel.GenerateNewSeed();
- // SamplerCardViewModel = vmFactory.Get();
- HiresFixSamplerCardViewModel = vmFactory.Get(vm =>
- {
- vm.IsScaleSizeMode = true;
- vm.IsCfgScaleEnabled = false;
- vm.IsSamplerSelectionEnabled = false;
- vm.IsDenoiseStrengthEnabled = true;
- });
+
ImageGalleryCardViewModel = vmFactory.Get();
PromptCardViewModel = vmFactory.Get();
- ConfigCardViewModel = vmFactory.Get().WithCards(new ViewModelBase[]
+ StackCardViewModel = vmFactory.Get();
+
+ StackCardViewModel.AddCards(new LoadableViewModelBase[]
+ {
+ // Sampler
+ vmFactory.Get(),
+ // Hires Fix
+ vmFactory.Get(stackExpander =>
{
- vmFactory.Get(),
- vmFactory.Get().WithCards(new ViewModelBase[]
+ stackExpander.Title = "Hires Fix";
+ stackExpander.AddCards(new LoadableViewModelBase[]
{
+ // Hires Fix Upscaler
vmFactory.Get(),
- vmFactory.Get(vm =>
+ // Hires Fix Sampler
+ vmFactory.Get(samplerCard =>
{
- vm.IsScaleSizeMode = true;
- vm.IsCfgScaleEnabled = false;
- vm.IsSamplerSelectionEnabled = false;
- vm.IsDenoiseStrengthEnabled = true;
- }),
- })
- });
+ samplerCard.IsDimensionsEnabled = false;
+ samplerCard.IsCfgScaleEnabled = false;
+ samplerCard.IsSamplerSelectionEnabled = false;
+ samplerCard.IsDenoiseStrengthEnabled = true;
+ })
+ });
+ })
+ });
}
private Dictionary GetCurrentPrompt()
{
+ var sampler = StackCardViewModel.GetCard();
+
var prompt = new Dictionary
{
["3"] = new()
@@ -107,16 +110,16 @@ public partial class InferenceTextToImageViewModel
ClassType = "KSampler",
Inputs = new Dictionary
{
- ["cfg"] = SamplerCardViewModel.CfgScale,
+ ["cfg"] = sampler.CfgScale,
["denoise"] = 1,
["latent_image"] = new object[] { "5", 0 },
["model"] = new object[] { "4", 0 },
["negative"] = new object[] { "7", 0 },
["positive"] = new object[] { "6", 0 },
- ["sampler_name"] = SamplerCardViewModel.SelectedSampler?.Name,
+ ["sampler_name"] = sampler.SelectedSampler?.Name,
["scheduler"] = "normal",
["seed"] = SeedCardViewModel.Seed,
- ["steps"] = SamplerCardViewModel.Steps
+ ["steps"] = sampler.Steps
}
},
["4"] = new()
@@ -130,8 +133,8 @@ public partial class InferenceTextToImageViewModel
Inputs = new Dictionary
{
["batch_size"] = BatchSize,
- ["height"] = SamplerCardViewModel.Height,
- ["width"] = SamplerCardViewModel.Width,
+ ["height"] = sampler.Height,
+ ["width"] = sampler.Width,
}
},
["6"] = new()
@@ -299,33 +302,35 @@ public partial class InferenceTextToImageViewModel
}
///
- public void LoadState(InferenceTextToImageModel state)
+ public override void LoadStateFromJsonObject(JsonObject state)
{
- SelectedModelName = state.SelectedModelName;
+ var model = DeserializeModel(state);
+
+ SelectedModelName = model.SelectedModelName;
- if (state.SeedCardState != null)
+ if (model.StackCardState != null)
{
- SeedCardViewModel.LoadState(state.SeedCardState);
+ StackCardViewModel.LoadStateFromJsonObject(model.StackCardState);
}
- if (state.SamplerCardState != null)
+ if (model.SeedCardState != null)
{
- SamplerCardViewModel.LoadState(state.SamplerCardState);
+ SeedCardViewModel.LoadStateFromJsonObject(model.SeedCardState);
}
- if (state.PromptCardState != null)
+ if (model.PromptCardState != null)
{
- PromptCardViewModel.LoadState(state.PromptCardState);
+ PromptCardViewModel.LoadStateFromJsonObject(model.PromptCardState);
}
}
///
- public InferenceTextToImageModel SaveState()
+ public override JsonObject SaveStateToJsonObject()
{
- return new InferenceTextToImageModel
+ return SerializeModel(new InferenceTextToImageModel
{
SelectedModelName = SelectedModelName,
- SeedCardState = SeedCardViewModel.SaveState(),
- SamplerCardState = SamplerCardViewModel.SaveState(),
- PromptCardState = PromptCardViewModel.SaveState(),
- };
+ StackCardState = StackCardViewModel.SaveStateToJsonObject(),
+ SeedCardState = SeedCardViewModel.SaveStateToJsonObject(),
+ PromptCardState = PromptCardViewModel.SaveStateToJsonObject()
+ });
}
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs
index 9f41040e..f1d70350 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs
@@ -1,31 +1,33 @@
-using AvaloniaEdit.Document;
+using System.Text.Json.Nodes;
+using AvaloniaEdit.Document;
using StabilityMatrix.Avalonia.Controls;
-using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Models.Inference;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(PromptCard))]
-public class PromptCardViewModel : ViewModelBase, ILoadableState
+public class PromptCardViewModel : LoadableViewModelBase
{
public TextDocument PromptDocument { get; } = new();
public TextDocument NegativePromptDocument { get; } = new();
///
- public void LoadState(PromptCardModel state)
+ public override void LoadStateFromJsonObject(JsonObject state)
{
- PromptDocument.Text = state.Prompt ?? "";
- NegativePromptDocument.Text = state.NegativePrompt ?? "";
+ var model = DeserializeModel(state);
+
+ PromptDocument.Text = model.Prompt ?? "";
+ NegativePromptDocument.Text = model.NegativePrompt ?? "";
}
///
- public PromptCardModel SaveState()
+ public override JsonObject SaveStateToJsonObject()
{
- return new PromptCardModel
+ return SerializeModel(new PromptCardModel
{
Prompt = PromptDocument.Text,
NegativePrompt = NegativePromptDocument.Text
- };
+ });
}
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs
index f1150032..c285c2ec 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Nodes;
using CommunityToolkit.Mvvm.ComponentModel;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Models;
@@ -10,7 +11,7 @@ using StabilityMatrix.Core.Models.Api.Comfy;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(SamplerCard))]
-public partial class SamplerCardViewModel : ViewModelBase, ILoadableState
+public partial class SamplerCardViewModel : LoadableViewModelBase
{
[ObservableProperty] private int steps = 20;
@@ -37,25 +38,27 @@ public partial class SamplerCardViewModel : ViewModelBase, ILoadableState
- public void LoadState(SamplerCardModel state)
+ public override void LoadStateFromJsonObject(JsonObject state)
{
- Steps = state.Steps;
- IsDenoiseStrengthEnabled = state.IsDenoiseStrengthEnabled;
- DenoiseStrength = state.DenoiseStrength;
- IsCfgScaleEnabled = state.IsCfgScaleEnabled;
- CfgScale = state.CfgScale;
- IsDimensionsEnabled = state.IsDimensionsEnabled;
- Width = state.Width;
- Height = state.Height;
- IsSamplerSelectionEnabled = state.IsSamplerSelectionEnabled;
- SelectedSampler = state.SelectedSampler is null ? null
- : new ComfySampler(state.SelectedSampler);
+ var model = DeserializeModel(state);
+
+ Steps = model.Steps;
+ IsDenoiseStrengthEnabled = model.IsDenoiseStrengthEnabled;
+ DenoiseStrength = model.DenoiseStrength;
+ IsCfgScaleEnabled = model.IsCfgScaleEnabled;
+ CfgScale = model.CfgScale;
+ IsDimensionsEnabled = model.IsDimensionsEnabled;
+ Width = model.Width;
+ Height = model.Height;
+ IsSamplerSelectionEnabled = model.IsSamplerSelectionEnabled;
+ SelectedSampler = model.SelectedSampler is null ? null
+ : new ComfySampler(model.SelectedSampler);
}
///
- public SamplerCardModel SaveState()
+ public override JsonObject SaveStateToJsonObject()
{
- return new SamplerCardModel
+ return SerializeModel(new SamplerCardModel
{
Steps = Steps,
IsDenoiseStrengthEnabled = IsDenoiseStrengthEnabled,
@@ -67,6 +70,6 @@ public partial class SamplerCardViewModel : ViewModelBase, ILoadableState
+public partial class SeedCardViewModel : LoadableViewModelBase
{
[ObservableProperty, NotifyPropertyChangedFor(nameof(RandomizeButtonToolTip))]
private bool isRandomizeEnabled = true;
@@ -28,19 +28,21 @@ public partial class SeedCardViewModel : ViewModelBase, ILoadableState
- public void LoadState(SeedCardModel state)
+ public override void LoadStateFromJsonObject(JsonObject state)
{
- Seed = long.TryParse(state.Seed, out var result) ? result : 0;
- IsRandomizeEnabled = state.IsRandomizeEnabled;
+ var model = DeserializeModel(state);
+
+ Seed = long.TryParse(model.Seed, out var result) ? result : 0;
+ IsRandomizeEnabled = model.IsRandomizeEnabled;
}
///
- public SeedCardModel SaveState()
+ public override JsonObject SaveStateToJsonObject()
{
- return new SeedCardModel
+ return SerializeModel(new SeedCardModel
{
Seed = Seed.ToString(),
IsRandomizeEnabled = IsRandomizeEnabled
- };
+ });
}
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs
index cdf20913..763054b2 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs
@@ -1,46 +1,37 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Collections;
+using System.Linq;
+using System.Text.Json.Nodes;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Models.Inference;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Extensions;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(StackCard))]
-public class StackCardViewModel : ViewModelBase
+public class StackCardViewModel : StackViewModelBase
{
- private readonly Dictionary> viewModelManager = new();
-
- public AvaloniaList ConfigCards { get; } = new();
-
- ///
- /// Register new cards
- ///
- public void AddCards(IEnumerable cards)
+ ///
+ public override void LoadStateFromJsonObject(JsonObject state)
{
- foreach (var card in cards)
+ var model = DeserializeModel(state);
+
+ if (model.Cards is null) return;
+
+ foreach (var (i, card) in model.Cards.Enumerate())
{
- var list = viewModelManager.GetOrAdd(card.GetType());
- list.Add(card);
- ConfigCards.Add(card);
+ // Ignore if more than cards than we have
+ if (i > Cards.Count - 1) break;
+
+ Cards[i].LoadStateFromJsonObject(card);
}
}
-
- ///
- /// Registers new cards and returns self
- ///
- public StackCardViewModel WithCards(IEnumerable cards)
- {
- AddCards(cards);
- return this;
- }
-
- ///
- /// Gets a card by type at specified index
- ///
- public T GetCard(int index = 0) where T : ViewModelBase
+
+ ///
+ public override JsonObject SaveStateToJsonObject()
{
- return (T) viewModelManager[typeof(T)][index];
+ return SerializeModel(new StackCardModel
+ {
+ Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
+ });
}
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs
index 6acd1298..f6366ece 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs
@@ -1,12 +1,45 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using System.Linq;
+using System.Text.Json.Nodes;
+using CommunityToolkit.Mvvm.ComponentModel;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Models.Inference;
using StabilityMatrix.Core.Attributes;
+using StabilityMatrix.Core.Extensions;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(StackExpander))]
-public partial class StackExpanderViewModel : StackCardViewModel
+public partial class StackExpanderViewModel : StackViewModelBase
{
[ObservableProperty] private string? title;
[ObservableProperty] private bool isEnabled;
+
+ ///
+ public override void LoadStateFromJsonObject(JsonObject state)
+ {
+ var model = DeserializeModel(state);
+ Title = model.Title;
+ IsEnabled = model.IsEnabled;
+
+ if (model.Cards is null) return;
+
+ foreach (var (i, card) in model.Cards.Enumerate())
+ {
+ // Ignore if more than cards than we have
+ if (i > Cards.Count - 1) break;
+
+ Cards[i].LoadStateFromJsonObject(card);
+ }
+ }
+
+ ///
+ public override JsonObject SaveStateToJsonObject()
+ {
+ return SerializeModel(new StackExpanderModel
+ {
+ Title = Title,
+ IsEnabled = IsEnabled,
+ Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
+ });
+ }
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/StackViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/StackViewModelBase.cs
new file mode 100644
index 00000000..c2e41e7f
--- /dev/null
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/StackViewModelBase.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Collections;
+using StabilityMatrix.Core.Extensions;
+
+namespace StabilityMatrix.Avalonia.ViewModels.Inference;
+
+public abstract class StackViewModelBase : LoadableViewModelBase
+{
+ private readonly Dictionary> viewModelManager = new();
+
+ public AvaloniaList Cards { get; } = new();
+
+ ///
+ /// Register new cards
+ ///
+ public void AddCards(IEnumerable cards)
+ {
+ foreach (var card in cards)
+ {
+ var list = viewModelManager.GetOrAdd(card.GetType());
+ list.Add(card);
+ Cards.Add(card);
+ }
+ }
+
+ ///
+ /// Registers new cards and returns self
+ ///
+ public StackViewModelBase WithCards(IEnumerable cards)
+ {
+ AddCards(cards);
+ return this;
+ }
+
+ ///
+ /// Gets a card by type at specified index
+ ///
+ public T GetCard(int index = 0) where T : LoadableViewModelBase
+ {
+ return (T) viewModelManager[typeof(T)][index];
+ }
+}
diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs
index 0a5d0452..1933efa6 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs
@@ -1,11 +1,43 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using System.Text.Json.Nodes;
+using CommunityToolkit.Mvvm.ComponentModel;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.Models.Inference;
+using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Core.Attributes;
+using StabilityMatrix.Core.Models.Api.Comfy;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(UpscalerCard))]
-public partial class UpscalerCardViewModel : ViewModelBase
+public partial class UpscalerCardViewModel : LoadableViewModelBase
{
[ObservableProperty] private double scale = 1;
+
+ [ObservableProperty] private ComfyUpscaler? selectedUpscaler;
+
+ public IInferenceClientManager ClientManager { get; }
+
+ public UpscalerCardViewModel(IInferenceClientManager clientManager)
+ {
+ ClientManager = clientManager;
+ }
+
+ ///
+ public override void LoadStateFromJsonObject(JsonObject state)
+ {
+ var model = DeserializeModel(state);
+
+ Scale = model.Scale;
+ SelectedUpscaler = model.SelectedUpscaler;
+ }
+
+ ///
+ public override JsonObject SaveStateToJsonObject()
+ {
+ return SerializeModel(new UpscalerCardModel
+ {
+ Scale = Scale,
+ SelectedUpscaler = SelectedUpscaler
+ });
+ }
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs
index c2b6ac21..a27659e7 100644
--- a/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs
@@ -7,6 +7,7 @@ using Avalonia.Controls.Notifications;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
+using Dock.Model.Mvvm.Controls;
using FluentAvalonia.UI.Controls;
using NLog;
using StabilityMatrix.Avalonia.Models;
@@ -50,10 +51,10 @@ public partial class InferenceViewModel : PageViewModelBase
public IInferenceClientManager ClientManager { get; }
- public AvaloniaList Tabs { get; } = new();
+ public AvaloniaList Tabs { get; } = new();
[ObservableProperty]
- private ViewModelBase? selectedTab;
+ private LoadableViewModelBase? selectedTab;
[ObservableProperty]
private PackagePair? runningPackage;
@@ -110,7 +111,7 @@ public partial class InferenceViewModel : PageViewModelBase
///
public void OnTabCloseRequested(TabViewTabCloseRequestedEventArgs e)
{
- if (e.Item is ViewModelBase vm)
+ if (e.Item is LoadableViewModelBase vm)
{
Tabs.Remove(vm);
}
@@ -259,17 +260,17 @@ public partial class InferenceViewModel : PageViewModelBase
await using var stream = await file.OpenReadAsync();
var document = await JsonSerializer.DeserializeAsync(stream);
- if (document == null)
+ if (document is null)
{
Logger.Warn("MenuOpenProject: Deserialize project file returned null");
return;
}
- ViewModelBase? vm = null;
- if (document.ProjectType is InferenceProjectType.TextToImage)
+ LoadableViewModelBase? vm = null;
+ if (document.ProjectType is InferenceProjectType.TextToImage && document.State is not null)
{
var textToImage = vmFactory.Get();
- textToImage.LoadState(document.State.Deserialize()!);
+ textToImage.LoadStateFromJsonObject(document.State);
vm = textToImage;
}
diff --git a/StabilityMatrix.Avalonia/ViewModels/LoadableViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/LoadableViewModelBase.cs
new file mode 100644
index 00000000..2c501715
--- /dev/null
+++ b/StabilityMatrix.Avalonia/ViewModels/LoadableViewModelBase.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using StabilityMatrix.Avalonia.Models;
+
+namespace StabilityMatrix.Avalonia.ViewModels;
+
+public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState
+{
+ ///
+ public abstract void LoadStateFromJsonObject(JsonObject state);
+
+ ///
+ public abstract JsonObject SaveStateToJsonObject();
+
+ ///
+ /// Serialize a model to a JSON object.
+ ///
+ protected static JsonObject SerializeModel(T model)
+ {
+ var node = JsonSerializer.SerializeToNode(model);
+ return node?.AsObject() ?? throw new
+ NullReferenceException("Failed to serialize state to JSON object.");
+ }
+
+ ///
+ /// Deserialize a model from a JSON object.
+ ///
+ protected static T DeserializeModel(JsonObject state)
+ {
+ return state.Deserialize() ?? throw new
+ NullReferenceException("Failed to deserialize state from JSON object.");
+ }
+}
diff --git a/StabilityMatrix.Avalonia/Views/InferencePage.axaml.cs b/StabilityMatrix.Avalonia/Views/InferencePage.axaml.cs
index e357ff6d..f94b4018 100644
--- a/StabilityMatrix.Avalonia/Views/InferencePage.axaml.cs
+++ b/StabilityMatrix.Avalonia/Views/InferencePage.axaml.cs
@@ -1,4 +1,5 @@
-using Avalonia.Markup.Xaml;
+using Avalonia.Input;
+using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.ViewModels;
@@ -10,6 +11,8 @@ public partial class InferencePage : UserControlBase
public InferencePage()
{
InitializeComponent();
+ AddHandler(DragDrop.DropEvent, DropHandler);
+ AddHandler(DragDrop.DragOverEvent, DragOverHandler);
}
private void InitializeComponent()
@@ -21,4 +24,20 @@ public partial class InferencePage : UserControlBase
{
(DataContext as InferenceViewModel)?.OnTabCloseRequested(args);
}
+
+ private void DragOverHandler(object? sender, DragEventArgs e)
+ {
+ if (DataContext is IDropTarget dropTarget)
+ {
+ dropTarget.DragOver(sender, e);
+ }
+ }
+
+ private void DropHandler(object? sender, DragEventArgs e)
+ {
+ if (DataContext is IDropTarget dropTarget)
+ {
+ dropTarget.Drop(sender, e);
+ }
+ }
}
diff --git a/StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml b/StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml
index 96221bdb..74f75dcd 100644
--- a/StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml
+++ b/StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml
@@ -88,7 +88,7 @@
x:DataType="Tool"
Id="ConfigTool">
+ DataContext="{ReflectionBinding ElementName=Dock, Path=DataContext.StackCardViewModel}"/>
@@ -122,7 +122,17 @@
Title="Image Output"
x:DataType="Tool"
Id="ImageGalleryTool">
-
+
+
+
+
+
+