Browse Source

Update State loading

pull/165/head
Ionite 1 year ago
parent
commit
279f11c1c3
No known key found for this signature in database
  1. 5
      StabilityMatrix.Avalonia/App.axaml
  2. 4
      StabilityMatrix.Avalonia/Controls/SamplerCard.axaml
  3. 2
      StabilityMatrix.Avalonia/Controls/StackCard.axaml
  4. 2
      StabilityMatrix.Avalonia/Controls/StackExpander.axaml
  5. 2
      StabilityMatrix.Avalonia/Controls/UpscalerCard.axaml
  6. 8
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  7. 10
      StabilityMatrix.Avalonia/Models/IJsonLoadableState.cs
  8. 17
      StabilityMatrix.Avalonia/Models/ILoadableState.cs
  9. 11
      StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs
  10. 11
      StabilityMatrix.Avalonia/Models/Inference/StackCardModel.cs
  11. 11
      StabilityMatrix.Avalonia/Models/Inference/StackExpanderModel.cs
  12. 11
      StabilityMatrix.Avalonia/Models/Inference/UpscalerCardModel.cs
  13. 26
      StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs
  14. 93
      StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs
  15. 20
      StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs
  16. 35
      StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs
  17. 18
      StabilityMatrix.Avalonia/ViewModels/Inference/SeedCardViewModel.cs
  18. 53
      StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs
  19. 37
      StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs
  20. 43
      StabilityMatrix.Avalonia/ViewModels/Inference/StackViewModelBase.cs
  21. 36
      StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs
  22. 15
      StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs
  23. 34
      StabilityMatrix.Avalonia/ViewModels/LoadableViewModelBase.cs
  24. 21
      StabilityMatrix.Avalonia/Views/InferencePage.axaml.cs
  25. 14
      StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml

5
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">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
@ -23,6 +22,7 @@
<Application.Styles>
<styling:FluentAvaloniaTheme PreferUserAccentColor="True" UseSystemFontOnWindows="True" />
<StyleInclude Source="avares://Dock.Avalonia/Themes/DockFluentTheme.axaml" />
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml"/>
<StyleInclude Source="avares://AsyncImageLoader.Avalonia/AdvancedImage.axaml" />
<StyleInclude Source="Styles/ProgressRing.axaml"/>
@ -32,5 +32,8 @@
<StyleInclude Source="Controls/SamplerCard.axaml"/>
<StyleInclude Source="Controls/ImageGalleryCard.axaml"/>
<StyleInclude Source="Controls/PromptCard.axaml"/>
<StyleInclude Source="Controls/StackCard.axaml"/>
<StyleInclude Source="Controls/UpscalerCard.axaml"/>
<StyleInclude Source="Controls/StackExpander.axaml"/>
</Application.Styles>
</Application>

4
StabilityMatrix.Avalonia/Controls/SamplerCard.axaml

@ -77,7 +77,7 @@
<StackPanel>
<!-- Denoise Strength -->
<Grid IsVisible="{Binding IsScaleSizeMode}">
<Grid IsVisible="{Binding IsDenoiseStrengthEnabled}">
<StackPanel>
<Grid ColumnDefinitions="*,Auto">
<TextBlock
@ -104,7 +104,7 @@
<!-- Dimensions (Absolute) -->
<Grid
IsVisible="{Binding !IsScaleSizeMode}"
IsVisible="{Binding IsDimensionsEnabled}"
ColumnDefinitions="*,Auto,*"
RowDefinitions="Auto,*">
<TextBlock

2
StabilityMatrix.Avalonia/Controls/StackCard.axaml

@ -27,7 +27,7 @@
<ItemsControl
VerticalAlignment="Top"
ItemsSource="{Binding ConfigCards}">
ItemsSource="{Binding Cards}">
<ItemsControl.DataTemplates>
<local:ViewLocator/>

2
StabilityMatrix.Avalonia/Controls/StackExpander.axaml

@ -38,7 +38,7 @@
<ItemsControl
VerticalAlignment="Top"
ItemsSource="{Binding ConfigCards}">
ItemsSource="{Binding Cards}">
<ItemsControl.DataTemplates>
<local:ViewLocator/>

2
StabilityMatrix.Avalonia/Controls/UpscalerCard.axaml

@ -18,7 +18,7 @@
<Setter Property="Template">
<ControlTemplate>
<controls:Card>
<StackPanel>
<StackPanel Spacing="8">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="*,*,*">
<!-- Sampler -->
<TextBlock

8
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -367,6 +367,8 @@ public static class DesignData
{
vm.Steps = 20;
vm.CfgScale = 7;
vm.IsDenoiseStrengthEnabled = false;
vm.IsDimensionsEnabled = true;
vm.SelectedSampler = new ComfySampler("euler");
});
@ -375,7 +377,7 @@ public static class DesignData
vm.Steps = 20;
vm.CfgScale = 7;
vm.SelectedSampler = new ComfySampler("euler");
vm.IsScaleSizeMode = true;
vm.IsDimensionsEnabled = false;
vm.IsCfgScaleEnabled = false;
vm.IsSamplerSelectionEnabled = false;
vm.IsDenoiseStrengthEnabled = true;
@ -402,7 +404,7 @@ public static class DesignData
public static StackCardViewModel StackCardViewModel =>
DialogFactory.Get<StackCardViewModel>(vm =>
{
vm.AddCards(new ViewModelBase[]
vm.AddCards(new LoadableViewModelBase[]
{
SamplerCardViewModel,
SeedCardViewModel,
@ -413,7 +415,7 @@ public static class DesignData
DialogFactory.Get<StackExpanderViewModel>(vm =>
{
vm.Title = "Hires Fix";
vm.AddCards(new ViewModelBase[]
vm.AddCards(new LoadableViewModelBase[]
{
SamplerCardViewModel,
SeedCardViewModel,

10
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();
}

17
StabilityMatrix.Avalonia/Models/ILoadableState.cs

@ -4,16 +4,23 @@ using System.Text.Json.Nodes;
namespace StabilityMatrix.Avalonia.Models;
public interface ILoadableState<T>
public interface ILoadableState<T> : 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.");
}
}

11
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; }
}

11
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<JsonObject>? Cards { get; init; }
}

11
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; }
}

11
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; }
}

26
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()

93
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<InferenceTextToImageModel>
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>();
SeedCardViewModel.GenerateNewSeed();
// SamplerCardViewModel = vmFactory.Get<SamplerCardViewModel>();
HiresFixSamplerCardViewModel = vmFactory.Get<SamplerCardViewModel>(vm =>
{
vm.IsScaleSizeMode = true;
vm.IsCfgScaleEnabled = false;
vm.IsSamplerSelectionEnabled = false;
vm.IsDenoiseStrengthEnabled = true;
});
ImageGalleryCardViewModel = vmFactory.Get<ImageGalleryCardViewModel>();
PromptCardViewModel = vmFactory.Get<PromptCardViewModel>();
ConfigCardViewModel = vmFactory.Get<StackCardViewModel>().WithCards(new ViewModelBase[]
StackCardViewModel = vmFactory.Get<StackCardViewModel>();
StackCardViewModel.AddCards(new LoadableViewModelBase[]
{
// Sampler
vmFactory.Get<SamplerCardViewModel>(),
// Hires Fix
vmFactory.Get<StackExpanderViewModel>(stackExpander =>
{
vmFactory.Get<SamplerCardViewModel>(),
vmFactory.Get<StackExpanderViewModel>().WithCards(new ViewModelBase[]
stackExpander.Title = "Hires Fix";
stackExpander.AddCards(new LoadableViewModelBase[]
{
// Hires Fix Upscaler
vmFactory.Get<UpscalerCardViewModel>(),
vmFactory.Get<SamplerCardViewModel>(vm =>
// Hires Fix Sampler
vmFactory.Get<SamplerCardViewModel>(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<string, ComfyNode> GetCurrentPrompt()
{
var sampler = StackCardViewModel.GetCard<SamplerCardViewModel>();
var prompt = new Dictionary<string, ComfyNode>
{
["3"] = new()
@ -107,16 +110,16 @@ public partial class InferenceTextToImageViewModel
ClassType = "KSampler",
Inputs = new Dictionary<string, object?>
{
["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<string, object?>
{
["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
}
/// <inheritdoc />
public void LoadState(InferenceTextToImageModel state)
public override void LoadStateFromJsonObject(JsonObject state)
{
SelectedModelName = state.SelectedModelName;
var model = DeserializeModel<InferenceTextToImageModel>(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);
}
}
/// <inheritdoc />
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()
});
}
}

20
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<PromptCardModel>
public class PromptCardViewModel : LoadableViewModelBase
{
public TextDocument PromptDocument { get; } = new();
public TextDocument NegativePromptDocument { get; } = new();
/// <inheritdoc />
public void LoadState(PromptCardModel state)
public override void LoadStateFromJsonObject(JsonObject state)
{
PromptDocument.Text = state.Prompt ?? "";
NegativePromptDocument.Text = state.NegativePrompt ?? "";
var model = DeserializeModel<PromptCardModel>(state);
PromptDocument.Text = model.Prompt ?? "";
NegativePromptDocument.Text = model.NegativePrompt ?? "";
}
/// <inheritdoc />
public PromptCardModel SaveState()
public override JsonObject SaveStateToJsonObject()
{
return new PromptCardModel
return SerializeModel(new PromptCardModel
{
Prompt = PromptDocument.Text,
NegativePrompt = NegativePromptDocument.Text
};
});
}
}

35
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<SamplerCardModel>
public partial class SamplerCardViewModel : LoadableViewModelBase
{
[ObservableProperty] private int steps = 20;
@ -37,25 +38,27 @@ public partial class SamplerCardViewModel : ViewModelBase, ILoadableState<Sample
}
/// <inheritdoc />
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<SamplerCardModel>(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);
}
/// <inheritdoc />
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<Sample
Height = Height,
IsSamplerSelectionEnabled = IsSamplerSelectionEnabled,
SelectedSampler = SelectedSampler?.Name
};
});
}
}

18
StabilityMatrix.Avalonia/ViewModels/Inference/SeedCardViewModel.cs

@ -1,15 +1,15 @@
using System;
using System.Text.Json.Nodes;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Models.Inference;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[View(typeof(SeedCard))]
public partial class SeedCardViewModel : ViewModelBase, ILoadableState<SeedCardModel>
public partial class SeedCardViewModel : LoadableViewModelBase
{
[ObservableProperty, NotifyPropertyChangedFor(nameof(RandomizeButtonToolTip))]
private bool isRandomizeEnabled = true;
@ -28,19 +28,21 @@ public partial class SeedCardViewModel : ViewModelBase, ILoadableState<SeedCardM
}
/// <inheritdoc />
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<SeedCardModel>(state);
Seed = long.TryParse(model.Seed, out var result) ? result : 0;
IsRandomizeEnabled = model.IsRandomizeEnabled;
}
/// <inheritdoc />
public SeedCardModel SaveState()
public override JsonObject SaveStateToJsonObject()
{
return new SeedCardModel
return SerializeModel(new SeedCardModel
{
Seed = Seed.ToString(),
IsRandomizeEnabled = IsRandomizeEnabled
};
});
}
}

53
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<Type, List<ViewModelBase>> viewModelManager = new();
public AvaloniaList<ViewModelBase> ConfigCards { get; } = new();
/// <summary>
/// Register new cards
/// </summary>
public void AddCards(IEnumerable<ViewModelBase> cards)
/// <inheritdoc />
public override void LoadStateFromJsonObject(JsonObject state)
{
foreach (var card in cards)
var model = DeserializeModel<StackCardModel>(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);
}
}
/// <summary>
/// Registers new cards and returns self
/// </summary>
public StackCardViewModel WithCards(IEnumerable<ViewModelBase> cards)
{
AddCards(cards);
return this;
}
/// <summary>
/// Gets a card by type at specified index
/// </summary>
public T GetCard<T>(int index = 0) where T : ViewModelBase
/// <inheritdoc />
public override JsonObject SaveStateToJsonObject()
{
return (T) viewModelManager[typeof(T)][index];
return SerializeModel(new StackCardModel
{
Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
});
}
}

37
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;
/// <inheritdoc />
public override void LoadStateFromJsonObject(JsonObject state)
{
var model = DeserializeModel<StackExpanderModel>(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);
}
}
/// <inheritdoc />
public override JsonObject SaveStateToJsonObject()
{
return SerializeModel(new StackExpanderModel
{
Title = Title,
IsEnabled = IsEnabled,
Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
});
}
}

43
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<Type, List<LoadableViewModelBase>> viewModelManager = new();
public AvaloniaList<LoadableViewModelBase> Cards { get; } = new();
/// <summary>
/// Register new cards
/// </summary>
public void AddCards(IEnumerable<LoadableViewModelBase> cards)
{
foreach (var card in cards)
{
var list = viewModelManager.GetOrAdd(card.GetType());
list.Add(card);
Cards.Add(card);
}
}
/// <summary>
/// Registers new cards and returns self
/// </summary>
public StackViewModelBase WithCards(IEnumerable<LoadableViewModelBase> cards)
{
AddCards(cards);
return this;
}
/// <summary>
/// Gets a card by type at specified index
/// </summary>
public T GetCard<T>(int index = 0) where T : LoadableViewModelBase
{
return (T) viewModelManager[typeof(T)][index];
}
}

36
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;
}
/// <inheritdoc />
public override void LoadStateFromJsonObject(JsonObject state)
{
var model = DeserializeModel<UpscalerCardModel>(state);
Scale = model.Scale;
SelectedUpscaler = model.SelectedUpscaler;
}
/// <inheritdoc />
public override JsonObject SaveStateToJsonObject()
{
return SerializeModel(new UpscalerCardModel
{
Scale = Scale,
SelectedUpscaler = SelectedUpscaler
});
}
}

15
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<ViewModelBase> Tabs { get; } = new();
public AvaloniaList<LoadableViewModelBase> Tabs { get; } = new();
[ObservableProperty]
private ViewModelBase? selectedTab;
private LoadableViewModelBase? selectedTab;
[ObservableProperty]
private PackagePair? runningPackage;
@ -110,7 +111,7 @@ public partial class InferenceViewModel : PageViewModelBase
/// </summary>
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<InferenceProjectDocument>(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<InferenceTextToImageViewModel>();
textToImage.LoadState(document.State.Deserialize<InferenceTextToImageModel>()!);
textToImage.LoadStateFromJsonObject(document.State);
vm = textToImage;
}

34
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
{
/// <inheritdoc />
public abstract void LoadStateFromJsonObject(JsonObject state);
/// <inheritdoc />
public abstract JsonObject SaveStateToJsonObject();
/// <summary>
/// Serialize a model to a JSON object.
/// </summary>
protected static JsonObject SerializeModel<T>(T model)
{
var node = JsonSerializer.SerializeToNode(model);
return node?.AsObject() ?? throw new
NullReferenceException("Failed to serialize state to JSON object.");
}
/// <summary>
/// Deserialize a model from a JSON object.
/// </summary>
protected static T DeserializeModel<T>(JsonObject state)
{
return state.Deserialize<T>() ?? throw new
NullReferenceException("Failed to deserialize state from JSON object.");
}
}

21
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);
}
}
}

14
StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml

@ -88,7 +88,7 @@
x:DataType="Tool"
Id="ConfigTool">
<controls:StackCard
DataContext="{ReflectionBinding ElementName=Dock, Path=DataContext.ConfigCardViewModel}"/>
DataContext="{ReflectionBinding ElementName=Dock, Path=DataContext.StackCardViewModel}"/>
</Tool>
</ToolDock>
@ -122,7 +122,17 @@
Title="Image Output"
x:DataType="Tool"
Id="ImageGalleryTool">
<controls:ImageGalleryCard DataContext="{ReflectionBinding ElementName=Dock, Path=DataContext.ImageGalleryCardViewModel}" />
<ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
FallbackColor="{DynamicResource ThemeBackgroundColor}"
TintColor="Black"
TintOpacity="0.6"
MaterialOpacity="0.8"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
<controls:ImageGalleryCard DataContext="{ReflectionBinding ElementName=Dock, Path=DataContext.ImageGalleryCardViewModel}" />
</ExperimentalAcrylicBorder>
</Tool>
</ToolDock>

Loading…
Cancel
Save