Browse Source

Add connected icon with info, UI formatting

pull/165/head
Ionite 1 year ago
parent
commit
def4c2b8a0
No known key found for this signature in database
  1. 38
      StabilityMatrix.Avalonia/Converters/UriStringConverter.cs
  2. 11
      StabilityMatrix.Avalonia/Models/InferenceProjectDocument.cs
  3. 37
      StabilityMatrix.Avalonia/Models/TagCompletion/CompletionProvider.cs
  4. 10
      StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs
  5. 35
      StabilityMatrix.Avalonia/ViewModels/Dialogs/InferenceConnectionHelpViewModel.cs
  6. 35
      StabilityMatrix.Avalonia/ViewModels/InferenceViewModel.cs
  7. 1
      StabilityMatrix.Avalonia/Views/Dialogs/InferenceConnectionHelpDialog.axaml
  8. 159
      StabilityMatrix.Avalonia/Views/InferencePage.axaml

38
StabilityMatrix.Avalonia/Converters/UriStringConverter.cs

@ -0,0 +1,38 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
namespace StabilityMatrix.Avalonia.Converters;
/// <summary>
/// Converts an Uri to a string, excluding leading protocol and trailing slashes
/// </summary>
public class UriStringConverter : IValueConverter
{
/// <inheritdoc />
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;
}
/// <inheritdoc />
public object ConvertBack(
object? value,
Type targetType,
object? parameter,
CultureInfo culture
)
{
throw new NotImplementedException();
}
}

11
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."
);
}
}
}

37
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<ICompletionData, string>? PrepareInsertionText =>
settingsManager.Settings.IsCompletionRemoveUnderscoresEnabled
? PrepareInsertionText_RemoveUnderscores
: null;
public Func<ICompletionData, string>? 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;
}
/// <inheritdoc />

10
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.

35
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<PackageManagerViewModel>(
param: new PackageManagerPage.PackageManagerNavigationOptions
{
OpenInstallerDialog = true,
InstallerSelectedPackage = packageFactory
.GetAllAvailablePackages()
.First(p => p is ComfyUI)
}
);
Dispatcher.UIThread.Post(() =>
{
navigationService.NavigateTo<PackageManagerViewModel>(
param: new PackageManagerPage.PackageManagerNavigationOptions
{
OpenInstallerDialog = true,
InstallerSelectedPackage = packageFactory
.GetAllAvailablePackages()
.First(p => p is ComfyUI)
}
);
});
}
/// <summary>
@ -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;
}
/// <inheritdoc />
public override void OnLoaded()
{
base.OnLoaded();
// Check if
}
}

35
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<ViewModelBase> 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<ViewModelBase> 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
);
}
}
}

1
StabilityMatrix.Avalonia/Views/Dialogs/InferenceConnectionHelpDialog.axaml

@ -43,6 +43,7 @@
<ComboBox
HorizontalAlignment="Stretch"
IsVisible="{Binding IsLaunchMode}"
ItemsSource="{Binding InstalledPackages}"
SelectedItem="{Binding SelectedPackage}">
<ComboBox.Styles>

159
StabilityMatrix.Avalonia/Views/InferencePage.axaml

@ -3,14 +3,16 @@
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"
@ -18,10 +20,11 @@
mc:Ignorable="d">
<controls:UserControlBase.Resources>
<ui:CommandBarFlyout Placement="Right" x:Key="AddTabFlyout">
<converters:UriStringConverter x:Key="UriStringConverter" />
<ui:CommandBarFlyout x:Key="AddTabFlyout" Placement="Right">
<!-- Note: unlike a regular CommandBar, primary items can be set as the xml content and don't need
to be wrapped in a <ui:CommandBarFlyout.PrimaryCommands> tag
-->
-->
<!--<ui:CommandBarButton Label="Text to Image" ToolTip.Tip="Text to Image">
<ui:CommandBarButton.IconSource>
<controls:FASymbolIconSource Symbol="fa-solid fa-bolt"/>
@ -31,56 +34,54 @@
<ui:CommandBarButton Label="Delete" ToolTip.Tip="Delete" />-->
<ui:CommandBarFlyout.SecondaryCommands>
<ui:CommandBarButton
Click="AddTabMenu_TextToImageButton_OnClick"
IconSource="FullScreenMaximize"
Label="Text to Image"
Click="AddTabMenu_TextToImageButton_OnClick"
ToolTip.Tip="Text to Image"/>
ToolTip.Tip="Text to Image" />
<ui:CommandBarButton
IsEnabled="False"
IconSource="ImageCopy"
IsEnabled="False"
Label="Image to Image"
ToolTip.Tip="Image to Image"/>
ToolTip.Tip="Image to Image" />
<ui:CommandBarButton
IsEnabled="False"
IconSource="ImageEdit"
IsEnabled="False"
Label="Inpaint"
ToolTip.Tip="Inpaint"/>
ToolTip.Tip="Inpaint" />
</ui:CommandBarFlyout.SecondaryCommands>
</ui:CommandBarFlyout>
</controls:UserControlBase.Resources>
<Grid>
<!-- AddTabButtonCommand="{Binding AddTabCommand}" -->
<ui:TabView
x:Name="TabView"
CanDragTabs="True"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AddTabButtonClick="TabView_OnAddTabButtonClick"
CanDragTabs="True"
CanReorderTabs="True"
CloseButtonOverlayMode="Auto"
SelectedIndex="{Binding SelectedTabIndex}"
SelectedItem="{Binding SelectedTab}"
TabCloseRequested="TabView_OnTabCloseRequested"
AddTabButtonClick="TabView_OnAddTabButtonClick"
TabItems="{Binding Tabs}"
SelectedItem="{Binding SelectedTab}"
SelectedIndex="{Binding SelectedTabIndex}"
TabWidthMode="SizeToContent">
<ui:TabView.TabItemTemplate>
<DataTemplate DataType="{x:Type vmInference:InferenceTextToImageViewModel}">
<ui:TabViewItem Header="{Binding TabTitle}"
IconSource="Document"
Content="{Binding}" />
<ui:TabViewItem
Content="{Binding FallbackValue=''}"
Header="{Binding TabTitle}"
IconSource="Document" />
</DataTemplate>
</ui:TabView.TabItemTemplate>
<ui:TabView.TabStripFooter>
<StackPanel
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="8"
HorizontalAlignment="Right">
Spacing="2">
<!-- Connecting Progress -->
<controls:ProgressRing
BorderThickness="4"
IsIndeterminate="True">
<!-- Connecting Progress -->
<controls:ProgressRing BorderThickness="4" IsIndeterminate="True">
<controls:ProgressRing.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.Or}">
<Binding Path="ClientManager.IsConnecting" />
@ -88,15 +89,13 @@
</MultiBinding>
</controls:ProgressRing.IsVisible>
</controls:ProgressRing>
<!-- Connecting Progress Text -->
<!-- Connecting Progress Text -->
<TextBlock
IsVisible="{Binding ClientManager.IsConnecting}"
VerticalAlignment="Center"
Text="{x:Static lang:Resources.Label_ConnectingEllipsis}"/>
<!-- Waiting to connect progress text -->
<TextBlock
VerticalAlignment="Center"
Text="{x:Static lang:Resources.Label_WaitingToConnectEllipsis}">
IsVisible="{Binding ClientManager.IsConnecting}"
Text="{x:Static lang:Resources.Label_ConnectingEllipsis}" />
<!-- Waiting to connect progress text -->
<TextBlock VerticalAlignment="Center" Text="{x:Static lang:Resources.Label_WaitingToConnectEllipsis}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!ClientManager.IsConnecting" />
@ -105,11 +104,11 @@
</Button.IsVisible>
</TextBlock>
<!-- Active connect (when Comfy is running) -->
<!-- Active connect (when Comfy is running) -->
<Button
Padding="12,4"
Command="{Binding ConnectCommand}"
Classes="success">
Classes="success"
Command="{Binding ConnectCommand}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="ClientManager.CanUserConnect" />
@ -118,17 +117,12 @@
</MultiBinding>
</Button.IsVisible>
<StackPanel Orientation="Horizontal">
<icons:Icon Value="fa-solid fa-circle-question"
Margin="0,0,8,0"
FontSize="14" />
<TextBlock Text="{x:Static lang:Resources.Action_Connect}" />
</StackPanel>
</Button>
<!-- Connect help -->
<Button
Padding="12,4"
Command="{Binding ShowConnectionHelpCommand}">
<!-- Connect help -->
<Button Padding="12,4" Command="{Binding ShowConnectionHelpCommand}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="ClientManager.CanUserConnect" />
@ -137,34 +131,67 @@
</MultiBinding>
</Button.IsVisible>
<StackPanel Orientation="Horizontal">
<icons:Icon Value="fa-solid fa-circle-question"
Margin="0,0,8,0"
FontSize="14" />
<TextBlock Text="{x:Static lang:Resources.Action_Connect}" />
<icons:Icon
Margin="0,0,8,0"
FontSize="14"
Value="fa-solid fa-circle-question" />
<TextBlock Text="{x:Static lang:Resources.Action_Launch}" />
</StackPanel>
</Button>
<Button
Classes="transparent"
BorderThickness="0">
<ui:SymbolIcon FontSize="18" Symbol="MoreVertical"/>
<!-- Successful connection icon -->
<Button Classes="transparent-full">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="ClientManager.IsConnected" />
<Binding Path="!ClientManager.IsConnecting" />
<Binding Path="!IsWaitingForConnection" />
</MultiBinding>
</Button.IsVisible>
<fluentIcons:SymbolIcon
VerticalAlignment="Center"
FontSize="18"
Foreground="{DynamicResource ThemeGreyColor}"
IsFilled="True"
Symbol="PlugConnected" />
<Button.Flyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<ui:MenuFlyoutItem
Command="{Binding DisconnectCommand}"
IsEnabled="False"
Text="{Binding ClientManager.Client.BaseAddress, Converter={StaticResource UriStringConverter}, FallbackValue=''}" />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem Command="{Binding DisconnectCommand}" Text="Disconnect">
<ui:MenuFlyoutItem.IconSource>
<fluentIcons:SymbolIconSource Symbol="PlugDisconnected" />
</ui:MenuFlyoutItem.IconSource>
</ui:MenuFlyoutItem>
</ui:FAMenuFlyout>
</Button.Flyout>
</Button>
<!-- Menu -->
<Button Classes="transparent-full">
<ui:SymbolIcon FontSize="18" Symbol="MoreVertical" />
<Button.Flyout>
<MenuFlyout Placement="BottomEdgeAlignedLeft">
<MenuItem Header="Open Project..."
Command="{Binding MenuOpenProjectCommand}"
InputGesture="Ctrl+O"/>
<Separator/>
<MenuItem Header="Save"
Command="{Binding MenuSaveCommand}"
InputGesture="Ctrl+S"/>
<MenuItem Header="Save As..."
Command="{Binding MenuSaveAsCommand}"
InputGesture="Ctrl+Shift+S"/>
<Separator IsVisible="{Binding ClientManager.IsConnected}"/>
<MenuItem Header="Disconnect"
Command="{Binding DisconnectCommand}"
IsVisible="{Binding ClientManager.IsConnected}"/>
</MenuFlyout>
<ui:FAMenuFlyout Placement="BottomEdgeAlignedRight">
<ui:MenuFlyoutItem
Command="{Binding MenuOpenProjectCommand}"
InputGesture="Ctrl+O"
Text="Open Project..." />
<ui:MenuFlyoutSeparator />
<ui:MenuFlyoutItem
Command="{Binding MenuSaveCommand}"
InputGesture="Ctrl+S"
Text="Save" />
<ui:MenuFlyoutItem
Command="{Binding MenuSaveAsCommand}"
InputGesture="Ctrl+Shift+S"
Text="Save As..." />
</ui:FAMenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>

Loading…
Cancel
Save