From def4c2b8a04f8fe66e748b2e0c22379a27b4229c Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 5 Sep 2023 18:23:35 -0400 Subject: [PATCH] Add connected icon with info, UI formatting --- .../Converters/UriStringConverter.cs | 38 ++++ .../Models/InferenceProjectDocument.cs | 11 ++ .../TagCompletion/CompletionProvider.cs | 37 +++- .../ViewModels/Base/LoadableViewModelBase.cs | 10 +- .../InferenceConnectionHelpViewModel.cs | 35 ++-- .../ViewModels/InferenceViewModel.cs | 35 +++- .../InferenceConnectionHelpDialog.axaml | 1 + .../Views/InferencePage.axaml | 175 ++++++++++-------- 8 files changed, 226 insertions(+), 116 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Converters/UriStringConverter.cs diff --git a/StabilityMatrix.Avalonia/Converters/UriStringConverter.cs b/StabilityMatrix.Avalonia/Converters/UriStringConverter.cs new file mode 100644 index 00000000..236828d9 --- /dev/null +++ b/StabilityMatrix.Avalonia/Converters/UriStringConverter.cs @@ -0,0 +1,38 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace StabilityMatrix.Avalonia.Converters; + +/// +/// Converts an Uri to a string, excluding leading protocol and trailing slashes +/// +public class UriStringConverter : IValueConverter +{ + /// + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is Uri uri) + { + return ( + uri.Host + + (uri.IsDefaultPort ? "" : $":{uri.Port}") + + uri.PathAndQuery + + uri.Fragment + ).TrimEnd('/'); + } + + return null; + } + + /// + public object ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture + ) + { + throw new NotImplementedException(); + } +} diff --git a/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs b/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs index fa977e13..a28420d0 100644 --- a/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs +++ b/StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs @@ -48,4 +48,15 @@ public class InferenceProjectDocument _ => throw new ArgumentOutOfRangeException(nameof(ProjectType), ProjectType, null) }; } + + public void VerifyVersion() + { + if (Version < 2) + { + throw new NotSupportedException( + $"Project was created in an earlier pre-release version of Stability Matrix and is no longer supported. " + + $"Please create a new project." + ); + } + } } diff --git a/StabilityMatrix.Avalonia/Models/TagCompletion/CompletionProvider.cs b/StabilityMatrix.Avalonia/Models/TagCompletion/CompletionProvider.cs index 4199b4f7..6b11778e 100644 --- a/StabilityMatrix.Avalonia/Models/TagCompletion/CompletionProvider.cs +++ b/StabilityMatrix.Avalonia/Models/TagCompletion/CompletionProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using AsyncAwaitBestPractices; using AutoComplete.Builders; @@ -25,10 +26,13 @@ using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.Models.TagCompletion; -public class CompletionProvider : ICompletionProvider +public partial class CompletionProvider : ICompletionProvider { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + [GeneratedRegex(@"([\[\]()<>])")] + private static partial Regex BracketsRegex(); + private readonly ISettingsManager settingsManager; private readonly INotificationService notificationService; private readonly IModelIndexService modelIndexService; @@ -40,10 +44,7 @@ public class CompletionProvider : ICompletionProvider public bool IsLoaded => searcher is not null; - public Func? PrepareInsertionText => - settingsManager.Settings.IsCompletionRemoveUnderscoresEnabled - ? PrepareInsertionText_RemoveUnderscores - : null; + public Func? PrepareInsertionText { get; } public CompletionProvider( ISettingsManager settingsManager, @@ -55,6 +56,8 @@ public class CompletionProvider : ICompletionProvider this.notificationService = notificationService; this.modelIndexService = modelIndexService; + PrepareInsertionText = PrepareInsertionText_Process; + // Attach to load from set file on initial settings load settingsManager.Loaded += (_, _) => UpdateTagCompletionCsv(); @@ -86,10 +89,28 @@ public class CompletionProvider : ICompletionProvider } } - private static string PrepareInsertionText_RemoveUnderscores(ICompletionData data) + private string PrepareInsertionText_Process(ICompletionData data) { - // Only for tags, skip and return text for other completion data - return data is not TagCompletionData ? data.Text : data.Text.Replace("_", " "); + var text = data.Text; + + // For tags and if enabled, replace underscores with spaces + if ( + data is TagCompletionData + && settingsManager.Settings.IsCompletionRemoveUnderscoresEnabled + ) + { + // Remove underscores + text = text.Replace("_", " "); + } + + // (Only for non model types) + // For bracket type character, escape it + if (data is not ModelCompletionData) + { + text = BracketsRegex().Replace(text, @"\$1"); + } + + return text; } /// diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs index 6aabb15c..d5986fcb 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs @@ -114,10 +114,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState property.PropertyType, jsonPropertyName.Name ); - if (property.GetValue(this) is string jsonPropertyNameValue) - { - name = jsonPropertyNameValue; - } + name = jsonPropertyName.Name; } // Check if property is in the JSON object @@ -227,10 +224,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState property.PropertyType, jsonPropertyName.Name ); - if (property.GetValue(this) is string value) - { - name = value; - } + name = jsonPropertyName.Name; } // For types that also implement IJsonLoadableState, defer to their implementation. diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs index 1897fd10..be5d5acf 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FluentAvalonia.UI.Controls; @@ -88,15 +89,18 @@ public partial class InferenceConnectionHelpViewModel : ContentDialogViewModelBa [RelayCommand] private void NavigateToInstall() { - navigationService.NavigateTo( - param: new PackageManagerPage.PackageManagerNavigationOptions - { - OpenInstallerDialog = true, - InstallerSelectedPackage = packageFactory - .GetAllAvailablePackages() - .First(p => p is ComfyUI) - } - ); + Dispatcher.UIThread.Post(() => + { + navigationService.NavigateTo( + param: new PackageManagerPage.PackageManagerNavigationOptions + { + OpenInstallerDialog = true, + InstallerSelectedPackage = packageFactory + .GetAllAvailablePackages() + .First(p => p is ComfyUI) + } + ); + }); } /// @@ -107,7 +111,10 @@ public partial class InferenceConnectionHelpViewModel : ContentDialogViewModelBa { if (SelectedPackage?.Id is { } id) { - EventManager.Instance.OnPackageLaunchRequested(id); + Dispatcher.UIThread.Post(() => + { + EventManager.Instance.OnPackageLaunchRequested(id); + }); } } @@ -129,12 +136,4 @@ public partial class InferenceConnectionHelpViewModel : ContentDialogViewModelBa return dialog; } - - /// - public override void OnLoaded() - { - base.OnLoaded(); - - // Check if - } } diff --git a/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs index f2b7a2b0..0789aa31 100644 --- a/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using AsyncAwaitBestPractices; using Avalonia.Controls; using Avalonia.Controls.Notifications; +using Avalonia.Controls.Shapes; using Avalonia.Platform.Storage; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; @@ -33,6 +34,7 @@ using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Services; +using Path = System.IO.Path; using Symbol = FluentIcons.Common.Symbol; using SymbolIconSource = FluentIcons.FluentAvalonia.SymbolIconSource; @@ -45,13 +47,13 @@ public partial class InferenceViewModel : PageViewModelBase private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly INotificationService notificationService; - // private readonly IRelayCommandFactory commandFactory; private readonly ISettingsManager settingsManager; private readonly ServiceManager vmFactory; - private readonly IApiFactory apiFactory; private readonly IModelIndexService modelIndexService; private readonly ILiteDbContext liteDbContext; + private bool isFirstLoadComplete; + public override string Title => "Inference"; public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.AppGeneric, IsFilled = true }; @@ -86,7 +88,6 @@ public partial class InferenceViewModel : PageViewModelBase public InferenceViewModel( ServiceManager vmFactory, - IApiFactory apiFactory, INotificationService notificationService, IInferenceClientManager inferenceClientManager, ISettingsManager settingsManager, @@ -95,7 +96,6 @@ public partial class InferenceViewModel : PageViewModelBase ) { this.vmFactory = vmFactory; - this.apiFactory = apiFactory; this.notificationService = notificationService; this.settingsManager = settingsManager; this.modelIndexService = modelIndexService; @@ -163,8 +163,6 @@ public partial class InferenceViewModel : PageViewModelBase }); } - private bool isFirstLoadComplete; - public override async Task OnLoadedAsync() { await base.OnLoadedAsync(); @@ -554,6 +552,8 @@ public partial class InferenceViewModel : PageViewModelBase throw new ApplicationException("Project file does not have 'State' key"); } + document.VerifyVersion(); + InferenceTabViewModelBase vm; if (document.ProjectType is InferenceProjectType.TextToImage) { @@ -615,8 +615,27 @@ public partial class InferenceViewModel : PageViewModelBase } // Load from file - var file = results[0]; + var file = results[0].TryGetLocalPath()!; - await AddTabFromFile(file.TryGetLocalPath()!); + try + { + await AddTabFromFile(file); + } + catch (NotSupportedException e) + { + notificationService.ShowPersistent( + $"Unsupported Project Version", + $"[{Path.GetFileName(file)}] {e.Message}", + NotificationType.Error + ); + } + catch (Exception e) + { + notificationService.ShowPersistent( + $"Failed to load Project", + $"[{Path.GetFileName(file)}] {e.Message}", + NotificationType.Error + ); + } } } diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/InferenceConnectionHelpDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/InferenceConnectionHelpDialog.axaml index 61016b70..c64594d8 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/InferenceConnectionHelpDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/InferenceConnectionHelpDialog.axaml @@ -43,6 +43,7 @@ diff --git a/StabilityMatrix.Avalonia/Views/InferencePage.axaml b/StabilityMatrix.Avalonia/Views/InferencePage.axaml index b5dde448..90cc75b6 100644 --- a/StabilityMatrix.Avalonia/Views/InferencePage.axaml +++ b/StabilityMatrix.Avalonia/Views/InferencePage.axaml @@ -3,25 +3,28 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls" + xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:fluentIcons="using:FluentIcons.FluentAvalonia" + xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" + xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData" - xmlns:vmInference="using:StabilityMatrix.Avalonia.ViewModels.Inference" xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels" - xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" - xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" + xmlns:vmInference="using:StabilityMatrix.Avalonia.ViewModels.Inference" d:DataContext="{x:Static mocks:DesignData.InferenceViewModel}" d:DesignHeight="650" d:DesignWidth="1000" x:DataType="vm:InferenceViewModel" mc:Ignorable="d"> - + - + + + --> - - + + ToolTip.Tip="Image to Image" /> + ToolTip.Tip="Inpaint" /> - + - - + - - - + Spacing="2"> + + + @@ -88,15 +89,13 @@ - - - + + IsVisible="{Binding ClientManager.IsConnecting}" + Text="{x:Static lang:Resources.Label_ConnectingEllipsis}" /> + + @@ -105,11 +104,11 @@ - + - - - - - + + +