Browse Source

mostly finished recommended models view

pull/438/head
JT 10 months ago
parent
commit
490745f77c
  1. 18
      StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
  2. 6
      StabilityMatrix.Avalonia/Languages/Resources.resx
  3. 2
      StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs
  4. 4
      StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelItemViewModel.cs
  5. 144
      StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs
  6. 21
      StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs
  7. 77
      StabilityMatrix.Avalonia/Views/Dialogs/RecommendedModelsDialog.axaml
  8. 8
      StabilityMatrix.Avalonia/Views/MainWindow.axaml
  9. 21
      StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
  10. 3
      StabilityMatrix.Core/Api/ILykosAuthApi.cs
  11. 4
      StabilityMatrix.Core/Helper/EventManager.cs
  12. 1
      StabilityMatrix.Core/Models/Settings/TeachingTip.cs

18
StabilityMatrix.Avalonia/Languages/Resources.Designer.cs generated

@ -212,6 +212,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to Download.
/// </summary>
public static string Action_Download {
get {
return ResourceManager.GetString("Action_Download", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit.
/// </summary>
@ -2426,6 +2435,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to Check the progress of your package installations and model downloads here..
/// </summary>
public static string TeachingTip_DownloadsExplanation {
get {
return ResourceManager.GetString("TeachingTip_DownloadsExplanation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Additional folders such as IPAdapters and TextualInversions (embeddings) can be enabled here.
/// </summary>

6
StabilityMatrix.Avalonia/Languages/Resources.resx

@ -942,4 +942,10 @@
<data name="Action_CopyDetails" xml:space="preserve">
<value>Copy Details</value>
</data>
<data name="Action_Download" xml:space="preserve">
<value>Download</value>
</data>
<data name="TeachingTip_DownloadsExplanation" xml:space="preserve">
<value>Check the progress of your package installations and model downloads here.</value>
</data>
</root>

2
StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs

@ -149,7 +149,7 @@ public partial class NewOneClickInstallViewModel : ContentDialogViewModelBase
var addInstalledPackageStep = new AddInstalledPackageStep(settingsManager, installedPackage);
steps.Add(addInstalledPackageStep);
var runner = new PackageModificationRunner { ShowDialogOnStart = true, HideCloseButton = true, };
var runner = new PackageModificationRunner { ShowDialogOnStart = false, HideCloseButton = false, };
EventManager.Instance.OnAddPackageInstallWithoutBlocking(this, runner, steps);
OnPrimaryButtonClick();

4
StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelItemViewModel.cs

@ -1,6 +1,7 @@
using System;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Models.Api;
@ -24,4 +25,7 @@ public partial class RecommendedModelItemViewModel : ViewModelBase
ModelVersion.Images?.FirstOrDefault()?.Url == null
? Assets.NoImage
: new Uri(ModelVersion.Images.First().Url);
[RelayCommand]
public void ToggleSelection() => IsSelected = !IsSelected;
}

144
StabilityMatrix.Avalonia/ViewModels/Dialogs/RecommendedModelsViewModel.cs

@ -1,30 +1,37 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
using DynamicData.Binding;
using LiteDB;
using LiteDB.Async;
using Microsoft.Extensions.Logging;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Api;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Database;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
[Transient]
[ManagedService]
public class RecommendedModelsViewModel : ContentDialogViewModelBase
public partial class RecommendedModelsViewModel : ContentDialogViewModelBase
{
private readonly ILogger<RecommendedModelsViewModel> logger;
private readonly ILykosAuthApi lykosApi;
private readonly ICivitApi civitApi;
private readonly ILiteDbContext liteDbContext;
private readonly ISettingsManager settingsManager;
private readonly INotificationService notificationService;
private readonly ITrackedDownloadService trackedDownloadService;
private readonly IDownloadService downloadService;
public SourceCache<RecommendedModelItemViewModel, int> CivitModels { get; } = new(p => p.ModelVersion.Id);
public IObservableCollection<RecommendedModelItemViewModel> Sd15Models { get; set; } =
@ -37,13 +44,21 @@ public class RecommendedModelsViewModel : ContentDialogViewModelBase
ILogger<RecommendedModelsViewModel> logger,
ILykosAuthApi lykosApi,
ICivitApi civitApi,
ILiteDbContext liteDbContext
ILiteDbContext liteDbContext,
ISettingsManager settingsManager,
INotificationService notificationService,
ITrackedDownloadService trackedDownloadService,
IDownloadService downloadService
)
{
this.logger = logger;
this.lykosApi = lykosApi;
this.civitApi = civitApi;
this.liteDbContext = liteDbContext;
this.settingsManager = settingsManager;
this.notificationService = notificationService;
this.trackedDownloadService = trackedDownloadService;
this.downloadService = downloadService;
CivitModels
.Connect()
@ -65,6 +80,125 @@ public class RecommendedModelsViewModel : ContentDialogViewModelBase
if (Design.IsDesignMode)
return;
// See if query is cached
CivitModelQueryCacheEntry? cachedQuery = null;
var recommendedModels = await lykosApi.GetRecommendedModels();
CivitModels.AddOrUpdate(
recommendedModels.Items.Select(
model =>
new RecommendedModelItemViewModel
{
ModelVersion = model.ModelVersions.First(
x => !x.BaseModel.Contains("Turbo", StringComparison.OrdinalIgnoreCase)
),
Author = $"by {model.Creator.Username}",
CivitModel = model
}
)
);
}
[RelayCommand]
private async Task DoImport()
{
var selectedModels = SdxlModels.Where(x => x.IsSelected).Concat(Sd15Models.Where(x => x.IsSelected));
foreach (var model in selectedModels)
{
// Get latest version file
var modelFile = model.ModelVersion.Files?.FirstOrDefault(
x => x is { Type: CivitFileType.Model, IsPrimary: true }
);
if (modelFile is null)
{
continue;
}
var rootModelsDirectory = new DirectoryPath(settingsManager.ModelsDirectory);
var downloadDirectory = rootModelsDirectory.JoinDir(
model.CivitModel.Type.ConvertTo<SharedFolderType>().GetStringValue()
);
// Folders might be missing if user didn't install any packages yet
downloadDirectory.Create();
var downloadPath = downloadDirectory.JoinFile(modelFile.Name);
// Download model info and preview first
var cmInfoPath = await SaveCmInfo(
model.CivitModel,
model.ModelVersion,
modelFile,
downloadDirectory
);
var previewImagePath = await SavePreviewImage(model.ModelVersion, downloadPath);
// Create tracked download
var download = trackedDownloadService.NewDownload(modelFile.DownloadUrl, downloadPath);
// Add hash info
download.ExpectedHashSha256 = modelFile.Hashes.SHA256;
// Add files to cleanup list
download.ExtraCleanupFileNames.Add(cmInfoPath);
if (previewImagePath is not null)
{
download.ExtraCleanupFileNames.Add(previewImagePath);
}
// Add hash context action
download.ContextAction = CivitPostDownloadContextAction.FromCivitFile(modelFile);
download.Start();
}
}
private static async Task<FilePath> SaveCmInfo(
CivitModel model,
CivitModelVersion modelVersion,
CivitFile modelFile,
DirectoryPath downloadDirectory
)
{
var modelFileName = Path.GetFileNameWithoutExtension(modelFile.Name);
var modelInfo = new ConnectedModelInfo(model, modelVersion, modelFile, DateTime.UtcNow);
await modelInfo.SaveJsonToDirectory(downloadDirectory, modelFileName);
var jsonName = $"{modelFileName}.cm-info.json";
return downloadDirectory.JoinFile(jsonName);
}
/// <summary>
/// Saves the preview image to the same directory as the model file
/// </summary>
/// <param name="modelVersion"></param>
/// <param name="modelFilePath"></param>
/// <returns>The file path of the saved preview image</returns>
private async Task<FilePath?> SavePreviewImage(CivitModelVersion modelVersion, FilePath modelFilePath)
{
// Skip if model has no images
if (modelVersion.Images == null || modelVersion.Images.Count == 0)
{
return null;
}
var image = modelVersion.Images[0];
var imageExtension = Path.GetExtension(image.Url).TrimStart('.');
if (imageExtension is "jpg" or "jpeg" or "png")
{
var imageDownloadPath = modelFilePath.Directory!.JoinFile(
$"{modelFilePath.NameWithoutExtension}.preview.{imageExtension}"
);
var imageTask = downloadService.DownloadToFileAsync(image.Url, imageDownloadPath);
await notificationService.TryAsync(imageTask, "Could not download preview image");
return imageDownloadPath;
}
return null;
}
}

21
StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs

@ -128,7 +128,7 @@ public partial class MainWindowViewModel : ViewModelBase
if (Program.Args.DebugOneClickInstall || settingsManager.Settings.InstalledPackages.Count == 0)
{
var viewModel = dialogFactory.Get<RecommendedModelsViewModel>();
var viewModel = dialogFactory.Get<NewOneClickInstallViewModel>();
var dialog = new BetterContentDialog
{
IsPrimaryButtonEnabled = false,
@ -136,7 +136,7 @@ public partial class MainWindowViewModel : ViewModelBase
IsFooterVisible = false,
FullSizeDesired = true,
MinDialogHeight = 775,
Content = new RecommendedModelsDialog { DataContext = viewModel },
Content = new NewOneClickInstallDialog { DataContext = viewModel },
};
EventManager.Instance.OneClickInstallFinished += (_, skipped) =>
@ -149,6 +149,23 @@ public partial class MainWindowViewModel : ViewModelBase
};
await dialog.ShowAsync(App.TopLevel);
var recommendedModelsViewModel = dialogFactory.Get<RecommendedModelsViewModel>();
dialog = new BetterContentDialog
{
IsPrimaryButtonEnabled = true,
FullSizeDesired = true,
MinDialogHeight = 900,
PrimaryButtonText = Resources.Action_Download,
CloseButtonText = Resources.Action_Close,
DefaultButton = ContentDialogButton.Primary,
PrimaryButtonCommand = recommendedModelsViewModel.DoImportCommand,
Content = new RecommendedModelsDialog { DataContext = recommendedModelsViewModel },
};
await dialog.ShowAsync(App.TopLevel);
EventManager.Instance.OnDownloadsTeachingTipRequested();
}
}

77
StabilityMatrix.Avalonia/Views/Dialogs/RecommendedModelsDialog.axaml

@ -11,17 +11,59 @@
xmlns:packages="clr-namespace:StabilityMatrix.Core.Models.Packages;assembly=StabilityMatrix.Core"
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
xmlns:api="clr-namespace:StabilityMatrix.Core.Models.Api;assembly=StabilityMatrix.Core"
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="725"
x:DataType="dialogs:RecommendedModelsViewModel"
d:DataContext="{x:Static mocks:DesignData.RecommendedModelsViewModel}"
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.RecommendedModelsDialog">
<controls:UserControlBase.Styles>
<Style Selector="Button#RootButton">
<Style Selector="^ asyncImageLoader|AdvancedImage">
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Property="RenderTransform"
Duration="0:0:0.237">
<TransformOperationsTransition.Easing>
<QuadraticEaseInOut/>
</TransformOperationsTransition.Easing>
</TransformOperationsTransition>
</Transitions>
</Setter>
</Style>
<Style Selector="^:pointerover">
<Setter Property="Cursor" Value="Hand" />
<Style Selector="^ asyncImageLoader|AdvancedImage">
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="RenderTransform" Value="scale(1.03, 1.03)"/>
</Style>
<Style Selector="^ Border#ModelCardBottom">
<Setter Property="Background" Value="#CC000000" />
</Style>
</Style>
<Style Selector="^:not(:pointerover)">
<Setter Property="Cursor" Value="Arrow" />
<Style Selector="^ asyncImageLoader|AdvancedImage">
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="RenderTransform" Value="scale(1, 1)"/>
</Style>
<Style Selector="^ Border#ModelCardBottom">
<Setter Property="Background" Value="#99000000" />
</Style>
</Style>
</Style>
</controls:UserControlBase.Styles>
<controls:UserControlBase.Resources>
<DataTemplate x:DataType="dialogs:RecommendedModelItemViewModel" x:Key="PackageTemplate">
<Button Padding="-4"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Margin="4"
Name="RootButton"
Classes="transparent-full"
Command="{Binding ToggleSelectionCommand}"
CornerRadius="8">
<controls:Card Margin="0" Padding="0">
@ -30,6 +72,7 @@
ZIndex="1"
VerticalAlignment="Top"
HorizontalAlignment="Right"
IsChecked="{Binding IsSelected}"
Margin="0,4,-8,0">
<CheckBox.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
@ -41,6 +84,7 @@
CornerRadius="8"
Width="200"
Height="250"
VerticalContentAlignment="Center"
Source="{Binding ThumbnailUrl}"
Stretch="UniformToFill"
StretchDirection="Both"/>
@ -50,10 +94,11 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#DD000000"
Name="ModelCardBottom"
CornerRadius="0,0,8,8"
ZIndex="1" >
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding ModelVersion.Name}"
<TextBlock Text="{Binding CivitModel.Name}"
VerticalAlignment="Center"
FontSize="14" />
<TextBlock Text="{Binding Author}"
@ -85,7 +130,7 @@
Margin="8"
FontSize="26"/>
<TextBlock Text="While your package is installing, here are some models we recommend to help get you started"
<TextBlock Text="While your package is installing, here are some models we recommend to help you get started"
TextAlignment="Center"
Margin="8,0,8,8"
TextWrapping="WrapWithOverflow"/>
@ -101,13 +146,14 @@
Margin="8,0,8,8"/>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
Margin="8">
<ItemsRepeater ItemsSource="{Binding Sd15Models}"
<ItemsControl ItemsSource="{Binding Sd15Models}"
ItemTemplate="{StaticResource PackageTemplate}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Horizontal"
Spacing="8"/>
</ItemsRepeater.Layout>
</ItemsRepeater>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
<TextBlock Text="Stable Diffusion XL"
@ -120,13 +166,14 @@
Margin="8,0,8,8"/>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
Margin="8,8">
<ItemsRepeater ItemsSource="{Binding SdxlModels}"
ItemTemplate="{StaticResource PackageTemplate}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Horizontal"
Spacing="8"/>
</ItemsRepeater.Layout>
</ItemsRepeater>
<ItemsControl ItemsSource="{Binding SdxlModels}"
ItemTemplate="{StaticResource PackageTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</controls:UserControlBase >

8
StabilityMatrix.Avalonia/Views/MainWindow.axaml

@ -161,5 +161,13 @@
Target="{Binding #FooterUpdateItem}"
Title="{x:Static lang:Resources.Label_UpdateAvailable}"
PreferredPlacement="Right" />
<ui:TeachingTip
Grid.Row="1"
Grid.RowSpan="2"
Name="DownloadsTeachingTip"
Target="{Binding #FooterDownloadItem}"
Title="{x:Static lang:Resources.Label_Downloads}"
PreferredPlacement="Right" />
</Grid>
</controls:AppWindowBase>

21
StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs

@ -97,6 +97,7 @@ public partial class MainWindow : AppWindowBase
EventManager.Instance.CultureChanged += (_, _) => SetDefaultFonts();
EventManager.Instance.UpdateAvailable += OnUpdateAvailable;
EventManager.Instance.NavigateAndFindCivitModelRequested += OnNavigateAndFindCivitModelRequested;
EventManager.Instance.DownloadsTeachingTipRequested += InstanceOnDownloadsTeachingTipRequested;
SetDefaultFonts();
@ -142,6 +143,26 @@ public partial class MainWindow : AppWindowBase
});
}
private void InstanceOnDownloadsTeachingTipRequested(object? sender, EventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
if (
!settingsManager.Settings.SeenTeachingTips.Contains(
Core.Models.Settings.TeachingTip.DownloadsTip
)
)
{
var target = this.FindControl<NavigationViewItem>("FooterDownloadItem")!;
var tip = this.FindControl<TeachingTip>("DownloadsTeachingTip")!;
tip.Target = target;
tip.Subtitle = Languages.Resources.TeachingTip_DownloadsExplanation;
tip.IsOpen = true;
}
});
}
private void OnNavigateAndFindCivitModelRequested(object? sender, int e)
{
navigationService.NavigateTo<CheckpointBrowserViewModel>();

3
StabilityMatrix.Core/Api/ILykosAuthApi.cs

@ -1,5 +1,6 @@
using System.Net;
using Refit;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Api.Lykos;
namespace StabilityMatrix.Core.Api;
@ -69,5 +70,5 @@ public interface ILykosAuthApi
);
[Get("/api/Models/recommended")]
Task<GetRecommendedModelsResponse> GetRecommendedModels();
Task<CivitModelsResponse> GetRecommendedModels();
}

4
StabilityMatrix.Core/Helper/EventManager.cs

@ -42,6 +42,7 @@ public class EventManager
public event EventHandler<LocalImageFile>? InferenceImageToImageRequested;
public event EventHandler<LocalImageFile>? InferenceImageToVideoRequested;
public event EventHandler<int>? NavigateAndFindCivitModelRequested;
public event EventHandler? DownloadsTeachingTipRequested;
public void OnGlobalProgressChanged(int progress) => GlobalProgressChanged?.Invoke(this, progress);
@ -98,4 +99,7 @@ public class EventManager
IPackageModificationRunner runner,
IReadOnlyList<IPackageStep> steps
) => AddPackageInstallWithoutBlocking?.Invoke(sender, runner, steps);
public void OnDownloadsTeachingTipRequested() =>
DownloadsTeachingTipRequested?.Invoke(this, EventArgs.Empty);
}

1
StabilityMatrix.Core/Models/Settings/TeachingTip.cs

@ -12,6 +12,7 @@ public record TeachingTip(string Value) : StringValue(Value)
public static TeachingTip AccountsCredentialsStorageNotice => new("AccountsCredentialsStorageNotice");
public static TeachingTip CheckpointCategoriesTip => new("CheckpointCategoriesTip");
public static TeachingTip PackageExtensionsInstallNotice => new("PackageExtensionsInstallNotice");
public static TeachingTip DownloadsTip => new("DownloadsTip");
/// <inheritdoc />
public override string ToString()

Loading…
Cancel
Save