Browse Source

Move prompt card to separate control

pull/165/head
Ionite 1 year ago
parent
commit
950838141e
No known key found for this signature in database
  1. 1
      StabilityMatrix.Avalonia/App.axaml
  2. 11
      StabilityMatrix.Avalonia/App.axaml.cs
  3. 59
      StabilityMatrix.Avalonia/Controls/PromptCard.axaml
  4. 80
      StabilityMatrix.Avalonia/Controls/PromptCard.axaml.cs
  5. 7
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  6. 11
      StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs
  7. 10
      StabilityMatrix.Avalonia/Models/Inference/PromptCardModel.cs
  8. 25
      StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs
  9. 31
      StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs
  10. 14
      StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs
  11. 43
      StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml
  12. 120
      StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml.cs

1
StabilityMatrix.Avalonia/App.axaml

@ -31,5 +31,6 @@
<StyleInclude Source="Controls/SeedCard.axaml"/>
<StyleInclude Source="Controls/SamplerCard.axaml"/>
<StyleInclude Source="Controls/ImageGalleryCard.axaml"/>
<StyleInclude Source="Controls/PromptCard.axaml"/>
</Application.Styles>
</Application>

11
StabilityMatrix.Avalonia/App.axaml.cs

@ -248,15 +248,18 @@ public sealed class App : Application
services.AddTransient<CheckpointFolder>();
services.AddTransient<CheckpointFile>();
services.AddTransient<InferenceTextToImageViewModel>();
services.AddTransient<SeedCardViewModel>();
services.AddTransient<SamplerCardViewModel>();
services.AddTransient<ImageGalleryCardViewModel>();
// Global progress
services.AddSingleton<ProgressManagerViewModel>();
// Controls
services.AddTransient<RefreshBadgeViewModel>();
// Inference controls
services.AddTransient<SeedCardViewModel>();
services.AddTransient<SamplerCardViewModel>();
services.AddTransient<ImageGalleryCardViewModel>();
services.AddTransient<PromptCardViewModel>();
// Dialog factory
services.AddSingleton<ServiceManager<ViewModelBase>>(provider =>
@ -277,6 +280,7 @@ public sealed class App : Application
.Register(provider.GetRequiredService<SeedCardViewModel>)
.Register(provider.GetRequiredService<SamplerCardViewModel>)
.Register(provider.GetRequiredService<ImageGalleryCardViewModel>)
.Register(provider.GetRequiredService<PromptCardViewModel>)
.Register(provider.GetRequiredService<FirstLaunchSetupViewModel>));
}
@ -298,6 +302,7 @@ public sealed class App : Application
services.AddTransient<ImageGalleryCard>();
services.AddTransient<SeedCard>();
services.AddTransient<SamplerCard>();
services.AddTransient<PromptCard>();
// Dialogs
services.AddTransient<SelectDataDirectoryDialog>();

59
StabilityMatrix.Avalonia/Controls/PromptCard.axaml

@ -0,0 +1,59 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:StabilityMatrix.Avalonia.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference"
x:DataType="vmInference:PromptCardViewModel">
<Design.PreviewWith>
<Grid Height="600" Width="600">
<controls:PromptCard DataContext="{x:Static mocks:DesignData.PromptCardViewModel}"/>
</Grid>
</Design.PreviewWith>
<Style Selector="controls|PromptCard">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<controls:Card Padding="8" HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<controls:Card.Styles>
<Style Selector="avaloniaEdit|TextEditor">
<Setter Property="Margin" Value="0,8,0,8"/>
<Setter Property="CornerRadius" Value="8" />
<Setter Property="LineNumbersForeground" Value="DarkSlateGray"/>
<Setter Property="ShowLineNumbers" Value="True"/>
<Setter Property="WordWrap" Value="True"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Background" Value="{DynamicResource ScrollBarTrackStroke}" />
</Style>
</controls:Card.Styles>
<Grid Margin="4,8" RowDefinitions="Auto,*,Auto,*">
<!-- Prompt -->
<TextBlock
Grid.Row="0"
Margin="4"
FontSize="14"
Text="Prompt" />
<avaloniaEdit:TextEditor
x:Name="PromptEditor"
Grid.Row="1"
Document="{Binding PromptDocument}"
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"/>
<!-- Negative Prompt -->
<TextBlock
Grid.Row="2"
Margin="4"
FontSize="14"
Text="Negative Prompt" />
<avaloniaEdit:TextEditor
x:Name="NegativePromptEditor"
Grid.Row="3"
Document="{Binding NegativePromptDocument}"
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"/>
</Grid>
</controls:Card>
</ControlTemplate>
</Setter>
</Style>
</Styles>

80
StabilityMatrix.Avalonia/Controls/PromptCard.axaml.cs

@ -0,0 +1,80 @@
using System.IO;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.TextMate;
using StabilityMatrix.Avalonia.Extensions;
using TextMateSharp.Grammars;
using TextMateSharp.Internal.Themes.Reader;
using TextMateSharp.Registry;
using TextMateSharp.Themes;
namespace StabilityMatrix.Avalonia.Controls;
public class PromptCard : TemplatedControl
{
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
InitializeEditors(e);
}
private static IRawTheme GetThemeFromStream(Stream stream)
{
using var reader = new StreamReader(stream);
return ThemeReader.ReadThemeSync(reader);
}
private static IRawTheme GetCustomTheme()
{
using var stream = Assets.ThemeMatrixDarkJson.Open();
return GetThemeFromStream(stream);
}
private void InitializeEditors(TemplateAppliedEventArgs e)
{
const ThemeName themeName = ThemeName.DimmedMonokai;
var registryOptions = new RegistryOptions(themeName);
var registry = new Registry(registryOptions);
using var stream = Assets.ImagePromptLanguageJson.Open();
var promptGrammar = registry.LoadGrammarFromStream(stream);
// Load theme
var theme = GetCustomTheme();
foreach (var editor in new[]
{
e.NameScope.Find<TextEditor>("PromptEditor"),
e.NameScope.Find<TextEditor>("NegativePromptEditor")
})
{
if (editor is not null)
{
var editorOptions = editor.Options;
editorOptions.ShowColumnRulers = true;
editorOptions.EnableTextDragDrop = true;
editorOptions.ExtendSelectionOnMouseUp = true;
// Config hyperlinks
editorOptions.EnableHyperlinks = true;
editorOptions.RequireControlModifierForHyperlinkClick = true;
editor.TextArea.TextView.LinkTextForegroundBrush = Brushes.Coral;
var installation = editor.InstallTextMate(registryOptions);
// Set the _textMateRegistry property
var field = typeof(TextMate.Installation).GetField("_textMateRegistry", BindingFlags.Instance | BindingFlags.NonPublic);
field!.SetValue(installation, registry);
installation.SetGrammar(promptGrammar.GetScopeName());
installation.SetTheme(theme);
}
}
}
}

7
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -392,8 +392,11 @@ public static class DesignData
});
});
public static Indexer Types => new();
public static PromptCardViewModel PromptCardViewModel =>
DialogFactory.Get<PromptCardViewModel>();
public static Indexer Types => new();
public class Indexer
{
public object? this[string typeName]

11
StabilityMatrix.Avalonia/Models/Inference/InferenceTextToImageModel.cs

@ -5,9 +5,10 @@ namespace StabilityMatrix.Avalonia.Models.Inference;
[JsonSerializable(typeof(InferenceTextToImageModel))]
public class InferenceTextToImageModel
{
public string? Prompt { get; set; }
public string? NegativePrompt { get; set; }
public string? SelectedModelName { get; set; }
public SeedCardModel? SeedCardState { get; set; }
public SamplerCardModel? SamplerCardState { get; set; }
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; }
}

10
StabilityMatrix.Avalonia/Models/Inference/PromptCardModel.cs

@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace StabilityMatrix.Avalonia.Models.Inference;
[JsonSerializable(typeof(PromptCardModel))]
public class PromptCardModel
{
public string? Prompt { get; set; }
public string? NegativePrompt { get; set; }
}

25
StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs

@ -18,7 +18,6 @@ using StabilityMatrix.Avalonia.Models.Inference;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Models.Api.Comfy;
using StabilityMatrix.Core.Models.Api.Comfy.WebSocketData;
@ -40,11 +39,7 @@ public partial class InferenceTextToImageViewModel
public SamplerCardViewModel SamplerCardViewModel { get; }
public SamplerCardViewModel HiresFixSamplerCardViewModel { get; }
public ImageGalleryCardViewModel ImageGalleryCardViewModel { get; }
public InferenceViewModel? Parent { get; set; }
public TextDocument PromptDocument { get; } = new();
public TextDocument NegativePromptDocument { get; } = new();
public PromptCardViewModel PromptCardViewModel { get; }
[ObservableProperty]
private string? selectedModelName;
@ -81,6 +76,7 @@ public partial class InferenceTextToImageViewModel
vm.IsDenoiseStrengthEnabled = true;
});
ImageGalleryCardViewModel = vmFactory.Get<ImageGalleryCardViewModel>();
PromptCardViewModel = vmFactory.Get<PromptCardViewModel>();
SeedCardViewModel.GenerateNewSeed();
}
@ -127,7 +123,7 @@ public partial class InferenceTextToImageViewModel
Inputs = new Dictionary<string, object?>
{
["clip"] = new object[] { "4", 1 },
["text"] = PromptDocument.Text,
["text"] = PromptCardViewModel.PromptDocument.Text,
}
},
["7"] = new()
@ -136,7 +132,7 @@ public partial class InferenceTextToImageViewModel
Inputs = new Dictionary<string, object?>
{
["clip"] = new object[] { "4", 1 },
["text"] = NegativePromptDocument.Text,
["text"] = PromptCardViewModel.NegativePromptDocument.Text,
}
},
["8"] = new()
@ -198,7 +194,6 @@ public partial class InferenceTextToImageViewModel
var nodes = GetCurrentPrompt();
// Connect progress handler
OutputProgress.IsIndeterminate = true;
client.ProgressUpdateReceived += OnProgressUpdateReceived;
client.PreviewImageReceived += OnPreviewImageReceived;
@ -265,6 +260,7 @@ public partial class InferenceTextToImageViewModel
{
// Disconnect progress handler
OutputProgress.Value = 0;
ImageGalleryCardViewModel.PreviewImage?.Dispose();
ImageGalleryCardViewModel.PreviewImage = null;
ImageGalleryCardViewModel.IsPreviewOverlayEnabled = false;
client.ProgressUpdateReceived -= OnProgressUpdateReceived;
@ -288,8 +284,6 @@ public partial class InferenceTextToImageViewModel
/// <inheritdoc />
public void LoadState(InferenceTextToImageModel state)
{
PromptDocument.Text = state.Prompt;
NegativePromptDocument.Text = state.NegativePrompt;
SelectedModelName = state.SelectedModelName;
if (state.SeedCardState != null)
@ -300,6 +294,10 @@ public partial class InferenceTextToImageViewModel
{
SamplerCardViewModel.LoadState(state.SamplerCardState);
}
if (state.PromptCardState != null)
{
PromptCardViewModel.LoadState(state.PromptCardState);
}
}
/// <inheritdoc />
@ -307,11 +305,10 @@ public partial class InferenceTextToImageViewModel
{
return new InferenceTextToImageModel
{
Prompt = PromptDocument.Text,
NegativePrompt = NegativePromptDocument.Text,
SelectedModelName = SelectedModelName,
SeedCardState = SeedCardViewModel.SaveState(),
SamplerCardState = SamplerCardViewModel.SaveState()
SamplerCardState = SamplerCardViewModel.SaveState(),
PromptCardState = PromptCardViewModel.SaveState(),
};
}
}

31
StabilityMatrix.Avalonia/ViewModels/Inference/PromptCardViewModel.cs

@ -0,0 +1,31 @@
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 TextDocument PromptDocument { get; } = new();
public TextDocument NegativePromptDocument { get; } = new();
/// <inheritdoc />
public void LoadState(PromptCardModel state)
{
PromptDocument.Text = state.Prompt ?? "";
NegativePromptDocument.Text = state.NegativePrompt ?? "";
}
/// <inheritdoc />
public PromptCardModel SaveState()
{
return new PromptCardModel
{
Prompt = PromptDocument.Text,
NegativePrompt = NegativePromptDocument.Text
};
}
}

14
StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs

@ -80,19 +80,11 @@ public partial class InferenceViewModel : PageViewModelBase
};
}
private InferenceTextToImageViewModel CreateTextToImageViewModel()
{
return vmFactory.Get<InferenceTextToImageViewModel>(vm =>
{
vm.Parent = this;
});
}
public override void OnLoaded()
{
if (Tabs.Count == 0)
{
Tabs.Add(CreateTextToImageViewModel());
AddTab();
}
// Select first tab if none is selected
@ -110,7 +102,7 @@ public partial class InferenceViewModel : PageViewModelBase
[RelayCommand]
private void AddTab()
{
Tabs.Add(CreateTextToImageViewModel());
Tabs.Add(vmFactory.Get<InferenceTextToImageViewModel>());
}
/// <summary>
@ -276,7 +268,7 @@ public partial class InferenceViewModel : PageViewModelBase
ViewModelBase? vm = null;
if (document.ProjectType is InferenceProjectType.TextToImage)
{
var textToImage = CreateTextToImageViewModel();
var textToImage = vmFactory.Get<InferenceTextToImageViewModel>();
textToImage.LoadState(document.State.Deserialize<InferenceTextToImageModel>()!);
vm = textToImage;
}

43
StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml

@ -140,47 +140,10 @@
Grid.Row="0"
Grid.Column="2"
Margin="8,8,8,16"
Grid.RowDefinitions="*,Auto">
RowDefinitions="*,Auto">
<!-- Prompt card -->
<controls:Card
Margin="0,0,0,4"
Padding="4"
DockPanel.Dock="Top">
<Grid Margin="4,8" RowDefinitions="Auto,*,Auto,*">
<TextBlock
Grid.Row="0"
Margin="4"
FontSize="14"
Text="Prompt" />
<avaloniaEdit:TextEditor
x:Name="PromptEditor"
Grid.Row="1"
Margin="0,8,0,8"
CornerRadius="8"
Document="{Binding PromptDocument}"
FontFamily="Jetbrains Mono,Cascadia Code,Consolas,Menlo,Monospace"
LineNumbersForeground="DarkSlateGray"
ShowLineNumbers="True"
VerticalScrollBarVisibility="Auto"
WordWrap="True" />
<TextBlock
Grid.Row="2"
Margin="4"
FontSize="14"
Text="Negative Prompt" />
<avaloniaEdit:TextEditor
x:Name="NegativePromptEditor"
Grid.Row="3"
Margin="0,8,0,8"
Document="{Binding NegativePromptDocument}"
FontFamily="Jetbrains Mono,Cascadia Code,Consolas,Menlo,Monospace"
LineNumbersForeground="DarkSlateGray"
ShowLineNumbers="True"
VerticalScrollBarVisibility="Auto"
WordWrap="True" />
</Grid>
</controls:Card>
<controls:PromptCard
DataContext="{Binding PromptCardViewModel}"/>
<StackPanel Grid.Row="1">
<!-- Generate Buttons -->

120
StabilityMatrix.Avalonia/Views/InferenceTextToImageView.axaml.cs

@ -1,26 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Xml;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.Highlighting;
using AvaloniaEdit.Highlighting.Xshd;
using AvaloniaEdit.TextMate;
using Markdown.Avalonia.SyntaxHigh.Extensions;
using Avalonia.Markup.Xaml;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Extensions;
using TextMateSharp.Grammars;
using TextMateSharp.Internal.Grammars.Reader;
using TextMateSharp.Internal.Themes.Reader;
using TextMateSharp.Internal.Types;
using TextMateSharp.Registry;
using TextMateSharp.Themes;
namespace StabilityMatrix.Avalonia.Views;
@ -35,101 +14,4 @@ public partial class InferenceTextToImageView : UserControlBase
{
AvaloniaXamlLoader.Load(this);
}
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
InitializeEditors();
}
private static IRawTheme GetThemeFromStream(Stream stream)
{
using var reader = new StreamReader(stream);
return ThemeReader.ReadThemeSync(reader);
}
private static IRawTheme GetCustomTheme()
{
using var stream = Assets.ThemeMatrixDarkJson.Open();
return GetThemeFromStream(stream);
}
private void InitializeEditors()
{
const ThemeName themeName = ThemeName.DimmedMonokai;
var registryOptions = new RegistryOptions(themeName);
var registry = new Registry(registryOptions);
using var stream = Assets.ImagePromptLanguageJson.Open();
var promptGrammar = registry.LoadGrammarFromStream(stream);
// Load theme
var theme = GetCustomTheme();
foreach (var editor in new[]
{
this.FindControl<TextEditor>("PromptEditor"),
this.FindControl<TextEditor>("NegativePromptEditor")
})
{
if (editor is not null)
{
var editorOptions = editor.Options;
editorOptions.ShowColumnRulers = true;
editorOptions.EnableTextDragDrop = true;
editorOptions.ExtendSelectionOnMouseUp = true;
// Config hyperlinks
editorOptions.EnableHyperlinks = true;
editorOptions.RequireControlModifierForHyperlinkClick = true;
editor.TextArea.TextView.LinkTextForegroundBrush = Brushes.Coral;
var installation = editor.InstallTextMate(registryOptions);
// Set the _textMateRegistry property
var field = typeof(TextMate.Installation).GetField("_textMateRegistry", BindingFlags.Instance | BindingFlags.NonPublic);
field!.SetValue(installation, registry);
installation.SetGrammar(promptGrammar.GetScopeName());
installation.SetTheme(theme);
}
}
}
/*private void InitializeEditorsForXshd()
{
var highlightManager = HighlightingManager.Instance;
using var stream = Assets.SDPromptXshd.Open();
using var reader = new XmlTextReader(stream);
highlightManager.RegisterHighlighting(
"ImagePrompt",
new []{ ".prompt" },
HighlightingLoader.Load(reader, HighlightingManager.Instance));
const ThemeName theme = ThemeName.DimmedMonokai;
foreach (var editor in new[]
{
this.FindControl<TextEditor>("PromptEditor"),
this.FindControl<TextEditor>("NegativePromptEditor")
})
{
if (editor is not null)
{
var editorOptions = editor.TextArea.Options;
// Config hyperlinks
editorOptions.EnableHyperlinks = true;
editorOptions.RequireControlModifierForHyperlinkClick = true;
editor.TextArea.TextView.LinkTextForegroundBrush = Brushes.Coral;
editor.SyntaxHighlighting = highlightManager.GetDefinition("ImagePrompt");
}
}
}*/
}

Loading…
Cancel
Save