Browse Source

Merge branch 'main' into inference

pull/165/head
Ionite 1 year ago committed by GitHub
parent
commit
e6ded40e73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      StabilityMatrix.Avalonia/Animations/BaseTransitionInfo.cs
  2. 59
      StabilityMatrix.Avalonia/Animations/BetterDrillInNavigationTransition.cs
  3. 66
      StabilityMatrix.Avalonia/Animations/BetterEntranceNavigationTransition.cs
  4. 96
      StabilityMatrix.Avalonia/Animations/BetterSlideNavigationTransition.cs
  5. 37
      StabilityMatrix.Avalonia/Services/NavigationService.cs
  6. 27
      StabilityMatrix.Avalonia/ViewLocator.cs
  7. 3
      StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs
  8. 79
      StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs
  9. 19
      StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs
  10. 2
      StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml
  11. 3
      StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
  12. 2
      StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml
  13. 128
      StabilityMatrix.Avalonia/Views/SettingsPage.axaml
  14. 8
      StabilityMatrix.Core/Helper/PrerequisiteHelper.cs
  15. 6
      StabilityMatrix.Core/Models/PackageVersion.cs
  16. 20
      StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs
  17. 2
      StabilityMatrix.Core/Models/Packages/ComfyUI.cs
  18. 132
      StabilityMatrix.Core/Models/Packages/InvokeAI.cs
  19. 35
      StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
  20. 2
      StabilityMatrix.Core/Models/Settings/Settings.cs

12
StabilityMatrix.Avalonia/Animations/BaseTransitionInfo.cs

@ -0,0 +1,12 @@
using System;
using FluentAvalonia.UI.Media.Animation;
namespace StabilityMatrix.Avalonia.Animations;
public abstract class BaseTransitionInfo : NavigationTransitionInfo
{
/// <summary>
/// The duration of the animation at 1x animation scale
/// </summary>
public abstract TimeSpan Duration { get; set; }
}

59
StabilityMatrix.Avalonia/Animations/BetterDrillInNavigationTransition.cs

@ -0,0 +1,59 @@
using System;
using System.Threading;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Media;
using Avalonia.Styling;
namespace StabilityMatrix.Avalonia.Animations;
public class BetterDrillInNavigationTransition : BaseTransitionInfo
{
/// <summary>
/// Gets or sets whether the animation should drill in (false) or drill out (true)
/// </summary>
public bool IsReversed { get; set; } = false; //Zoom out if true
public override TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(400);
public override async void RunAnimation(Animatable ctrl, CancellationToken cancellationToken)
{
var animation = new Animation
{
Easing = new SplineEasing(0.1, 0.9, 0.2, 1.0),
Children =
{
new KeyFrame
{
Setters =
{
new Setter(Visual.OpacityProperty, 0.0),
new Setter(ScaleTransform.ScaleXProperty, IsReversed ? 1.5 : 0.0),
new Setter(ScaleTransform.ScaleYProperty, IsReversed ? 1.5 : 0.0)
},
Cue = new Cue(0d)
},
new KeyFrame
{
Setters =
{
new Setter(Visual.OpacityProperty, 1.0),
new Setter(ScaleTransform.ScaleXProperty, IsReversed ? 1.0 : 1.0),
new Setter(ScaleTransform.ScaleYProperty, IsReversed ? 1.0 : 1.0)
},
Cue = new Cue(1d)
}
},
Duration = Duration,
FillMode = FillMode.Forward
};
await animation.RunAsync(ctrl, cancellationToken);
if (ctrl is Visual visualCtrl)
{
visualCtrl.Opacity = 1;
}
}
}

66
StabilityMatrix.Avalonia/Animations/BetterEntranceNavigationTransition.cs

@ -0,0 +1,66 @@
using System;
using System.Threading;
using AsyncAwaitBestPractices;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Media;
using Avalonia.Styling;
using FluentAvalonia.UI.Media.Animation;
namespace StabilityMatrix.Avalonia.Animations;
public class BetterEntranceNavigationTransition : BaseTransitionInfo
{
public override TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(500);
/// <summary>
/// Gets or sets the Horizontal Offset used when animating
/// </summary>
public double FromHorizontalOffset { get; set; } = 0;
/// <summary>
/// Gets or sets the Vertical Offset used when animating
/// </summary>
public double FromVerticalOffset { get; set; } = 100;
public override async void RunAnimation(Animatable ctrl, CancellationToken cancellationToken)
{
var animation = new Animation
{
Easing = new SplineEasing(0.1, 0.9, 0.2, 1.0),
Children =
{
new KeyFrame
{
Setters =
{
new Setter(Visual.OpacityProperty, 0.0),
new Setter(TranslateTransform.XProperty,FromHorizontalOffset),
new Setter(TranslateTransform.YProperty, FromVerticalOffset)
},
Cue = new Cue(0d)
},
new KeyFrame
{
Setters =
{
new Setter(Visual.OpacityProperty, 1d),
new Setter(TranslateTransform.XProperty,0.0),
new Setter(TranslateTransform.YProperty, 0.0)
},
Cue = new Cue(1d)
}
},
Duration = Duration,
FillMode = FillMode.Forward
};
await animation.RunAsync(ctrl, cancellationToken);
if (ctrl is Visual visualCtrl)
{
visualCtrl.Opacity = 1;
}
}
}

96
StabilityMatrix.Avalonia/Animations/BetterSlideNavigationTransition.cs

@ -0,0 +1,96 @@
using System;
using System.Threading;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Media;
using Avalonia.Styling;
using FluentAvalonia.UI.Media.Animation;
namespace StabilityMatrix.Avalonia.Animations;
public class BetterSlideNavigationTransition : BaseTransitionInfo
{
public override TimeSpan Duration { get; set; } = TimeSpan.FromMilliseconds(167);
/// <summary>
/// Gets or sets the type of animation effect to play during the slide transition.
/// </summary>
public SlideNavigationTransitionEffect Effect { get; set; } = SlideNavigationTransitionEffect.FromRight;
/// <summary>
/// Gets or sets the HorizontalOffset used when animating from the Left or Right
/// </summary>
public double FromHorizontalOffset { get; set; } = 56;
/// <summary>
/// Gets or sets the VerticalOffset used when animating from the Top or Bottom
/// </summary>
public double FromVerticalOffset { get; set; } = 56;
public override async void RunAnimation(Animatable ctrl, CancellationToken cancellationToken)
{
double length = 0;
bool isVertical = false;
switch (Effect)
{
case SlideNavigationTransitionEffect.FromLeft:
length = -FromHorizontalOffset;
break;
case SlideNavigationTransitionEffect.FromRight:
length = FromHorizontalOffset;
break;
case SlideNavigationTransitionEffect.FromTop:
length = -FromVerticalOffset;
isVertical = true;
break;
case SlideNavigationTransitionEffect.FromBottom:
length = FromVerticalOffset;
isVertical = true;
break;
}
var animation = new Animation
{
Easing = new SplineEasing(0.1, 0.9, 0.2, 1.0),
Children =
{
new KeyFrame
{
Setters =
{
new Setter(isVertical ? TranslateTransform.YProperty : TranslateTransform.XProperty, length),
new Setter(Visual.OpacityProperty, 0d)
},
Cue = new Cue(0d)
},
new KeyFrame
{
Setters=
{
new Setter(Visual.OpacityProperty, 1d)
},
Cue = new Cue(0.05d)
},
new KeyFrame
{
Setters =
{
new Setter(Visual.OpacityProperty, 1d),
new Setter(isVertical ? TranslateTransform.YProperty : TranslateTransform.XProperty, 0.0)
},
Cue = new Cue(1d)
}
},
Duration = Duration,
FillMode = FillMode.Forward
};
await animation.RunAsync(ctrl, cancellationToken);
if (ctrl is Visual visual)
{
visual.Opacity = 1;
}
}
}

37
StabilityMatrix.Avalonia/Services/NavigationService.cs

@ -1,10 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Media.Animation; using FluentAvalonia.UI.Media.Animation;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
using StabilityMatrix.Avalonia.Animations;
using StabilityMatrix.Avalonia.ViewModels; using StabilityMatrix.Avalonia.ViewModels;
using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.Services; namespace StabilityMatrix.Avalonia.Services;
@ -26,6 +30,23 @@ public class NavigationService : INavigationService
throw new InvalidOperationException("SetFrame was not called before NavigateTo."); throw new InvalidOperationException("SetFrame was not called before NavigateTo.");
} }
if (App.Services.GetService(typeof(ISettingsManager)) is ISettingsManager settingsManager)
{
// Handle animation scale
switch (transitionInfo)
{
// If the transition info is null or animation scale is 0, suppress the transition
case null:
case BaseTransitionInfo when settingsManager.Settings.AnimationScale == 0f:
transitionInfo = new SuppressNavigationTransitionInfo();
break;
case BaseTransitionInfo baseTransitionInfo:
baseTransitionInfo.Duration *= settingsManager.Settings.AnimationScale;
break;
}
}
_frame.NavigateToType(typeof(TViewModel), _frame.NavigateToType(typeof(TViewModel),
null, null,
new FrameNavigationOptions new FrameNavigationOptions
@ -52,6 +73,22 @@ public class NavigationService : INavigationService
throw new InvalidOperationException("SetFrame was not called before NavigateTo."); throw new InvalidOperationException("SetFrame was not called before NavigateTo.");
} }
if (App.Services.GetService(typeof(ISettingsManager)) is ISettingsManager settingsManager)
{
// Handle animation scale
switch (transitionInfo)
{
// If the transition info is null or animation scale is 0, suppress the transition
case null:
case BaseTransitionInfo when settingsManager.Settings.AnimationScale == 0f:
transitionInfo = new SuppressNavigationTransitionInfo();
break;
case BaseTransitionInfo baseTransitionInfo:
baseTransitionInfo.Duration *= settingsManager.Settings.AnimationScale;
break;
}
}
_frame.NavigateFromObject(viewModel, _frame.NavigateFromObject(viewModel,
new FrameNavigationOptions new FrameNavigationOptions
{ {

27
StabilityMatrix.Avalonia/ViewLocator.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
@ -51,28 +52,30 @@ public class ViewLocator : IDataTemplate, INavigationPageFactory
/// <inheritdoc /> /// <inheritdoc />
public Control? GetPage(Type srcType) public Control? GetPage(Type srcType)
{ {
if (Attribute.GetCustomAttribute(srcType, typeof(ViewAttribute)) is ViewAttribute viewAttr) if (Attribute.GetCustomAttribute(srcType, typeof(ViewAttribute)) is not ViewAttribute
viewAttr)
{ {
var viewType = viewAttr.GetViewType(); throw new InvalidOperationException("View not found for " + srcType.FullName);
var view = GetView(viewType);
view.DataContext ??= App.Services.GetService(srcType);
return view;
} }
throw new InvalidOperationException("View not found for " + srcType.FullName); var viewType = viewAttr.GetViewType();
var view = GetView(viewType);
view.DataContext ??= App.Services.GetService(srcType);
return view;
} }
/// <inheritdoc /> /// <inheritdoc />
public Control GetPageFromObject(object target) public Control GetPageFromObject(object target)
{ {
if (Attribute.GetCustomAttribute(target.GetType(), typeof(ViewAttribute)) is ViewAttribute viewAttr) if (Attribute.GetCustomAttribute(target.GetType(), typeof(ViewAttribute)) is not
ViewAttribute viewAttr)
{ {
var viewType = viewAttr.GetViewType(); throw new InvalidOperationException("View not found for " + target.GetType().FullName);
var view = GetView(viewType);
view.DataContext ??= target;
return view;
} }
throw new InvalidOperationException("View not found for " + target.GetType().FullName); var viewType = viewAttr.GetViewType();
var view = GetView(viewType);
view.DataContext ??= target;
return view;
} }
} }

3
StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs

@ -38,7 +38,6 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
private readonly IPyRunner pyRunner; private readonly IPyRunner pyRunner;
private readonly IDownloadService downloadService; private readonly IDownloadService downloadService;
private readonly INotificationService notificationService; private readonly INotificationService notificationService;
private readonly ISharedFolders sharedFolders;
private readonly IPrerequisiteHelper prerequisiteHelper; private readonly IPrerequisiteHelper prerequisiteHelper;
[ObservableProperty] private BasePackage selectedPackage; [ObservableProperty] private BasePackage selectedPackage;
@ -82,14 +81,12 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
IPackageFactory packageFactory, IPackageFactory packageFactory,
IPyRunner pyRunner, IPyRunner pyRunner,
IDownloadService downloadService, INotificationService notificationService, IDownloadService downloadService, INotificationService notificationService,
ISharedFolders sharedFolders,
IPrerequisiteHelper prerequisiteHelper) IPrerequisiteHelper prerequisiteHelper)
{ {
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.pyRunner = pyRunner; this.pyRunner = pyRunner;
this.downloadService = downloadService; this.downloadService = downloadService;
this.notificationService = notificationService; this.notificationService = notificationService;
this.sharedFolders = sharedFolders;
this.prerequisiteHelper = prerequisiteHelper; this.prerequisiteHelper = prerequisiteHelper;
// AvailablePackages and SelectedPackage // AvailablePackages and SelectedPackage

79
StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs

@ -1,16 +1,17 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Media.Animation;
using NLog; using NLog;
using Polly; using Polly;
using StabilityMatrix.Avalonia.Animations;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models;
@ -20,7 +21,7 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.PackageManager; namespace StabilityMatrix.Avalonia.ViewModels.PackageManager;
public partial class PackageCardViewModel : Base.ProgressViewModel public partial class PackageCardViewModel : ProgressViewModel
{ {
private readonly IPackageFactory packageFactory; private readonly IPackageFactory packageFactory;
private readonly INotificationService notificationService; private readonly INotificationService notificationService;
@ -67,7 +68,7 @@ public partial class PackageCardViewModel : Base.ProgressViewModel
settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id); settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id);
navigationService.NavigateTo<LaunchPageViewModel>(new EntranceNavigationTransitionInfo()); navigationService.NavigateTo<LaunchPageViewModel>(new BetterDrillInNavigationTransition());
EventManager.Instance.OnPackageLaunchRequested(Package.Id); EventManager.Instance.OnPackageLaunchRequested(Package.Id);
} }
@ -117,65 +118,61 @@ public partial class PackageCardViewModel : Base.ProgressViewModel
public async Task Update() public async Task Update()
{ {
if (Package == null) return; if (Package == null) return;
var basePackage = packageFactory[Package.PackageName!]; var basePackage = packageFactory[Package.PackageName!];
if (basePackage == null) if (basePackage == null)
{ {
logger.Error("Could not find package {SelectedPackagePackageName}", logger.Warn("Could not find package {SelectedPackagePackageName}",
Package.PackageName); Package.PackageName);
notificationService.Show("Invalid Package type",
$"Package {Package.PackageName.ToRepr()} is not a valid package type",
NotificationType.Error);
return; return;
} }
Text = $"Updating {Package.DisplayName}"; Text = $"Updating {Package.DisplayName}";
IsIndeterminate = true; IsIndeterminate = true;
basePackage.InstallLocation = Package.FullPath!; try
var progress = new Progress<ProgressReport>(progress =>
{ {
var percent = Convert.ToInt32(progress.Percentage); basePackage.InstallLocation = Package.FullPath!;
var progress = new Progress<ProgressReport>(progress =>
{
var percent = Convert.ToInt32(progress.Percentage);
Value = percent; Value = percent;
IsIndeterminate = progress.IsIndeterminate; IsIndeterminate = progress.IsIndeterminate;
Text = $"Updating {Package.DisplayName}"; Text = $"Updating {Package.DisplayName}";
EventManager.Instance.OnGlobalProgressChanged(percent); EventManager.Instance.OnGlobalProgressChanged(percent);
}); });
var updateResult = await basePackage.Update(Package, progress); var updateResult = await basePackage.Update(Package, progress);
if (string.IsNullOrWhiteSpace(updateResult)) settingsManager.UpdatePackageVersionNumber(Package.Id, updateResult);
{ notificationService.Show("Update complete",
var errorMsg = $"{Package.DisplayName} has been updated to the latest version.",
$"There was an error updating {Package.DisplayName}. Please try again later."; NotificationType.Success);
if (Package.PackageName == "automatic") await using (settingsManager.BeginTransaction())
{ {
errorMsg = errorMsg.Replace("Please try again later.", Package.UpdateAvailable = false;
"Please stash any changes before updating, or manually update the package.");
} }
IsUpdateAvailable = false;
// there was an error InstalledVersion = Package.DisplayVersion ?? "Unknown";
notificationService.Show(new Notification("Error updating package",
errorMsg, NotificationType.Error));
} }
catch (Exception e)
settingsManager.UpdatePackageVersionNumber(Package.Id, updateResult); {
notificationService.Show("Update complete", logger.Error(e, "Error Updating Package ({PackageName})", basePackage.Name);
$"{Package.DisplayName} has been updated to the latest version.", notificationService.ShowPersistent($"Error Updating {Package.DisplayName}", e.Message, NotificationType.Error);
NotificationType.Success); }
finally
Dispatcher.UIThread.Invoke(() =>
{ {
using (settingsManager.BeginTransaction())
{
Package.UpdateAvailable = false;
}
IsUpdateAvailable = false;
IsIndeterminate = false; IsIndeterminate = false;
InstalledVersion = settingsManager.Settings.InstalledPackages
.First(x => x.Id == Package.Id).DisplayVersion ?? "Unknown";
Value = 0; Value = 0;
}); Text = "";
}
} }
public async Task OpenFolder() public async Task OpenFolder()

19
StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs

@ -73,6 +73,21 @@ public partial class SettingsViewModel : PageViewModelBase
"System", "System",
}; };
public IReadOnlyList<float> AnimationScaleOptions { get; } = new[]
{
0f,
0.25f,
0.5f,
0.75f,
1f,
1.25f,
1.5f,
1.75f,
2f,
};
[ObservableProperty] private float selectedAnimationScale;
// Shared folder options // Shared folder options
[ObservableProperty] private bool removeSymlinksOnShutdown; [ObservableProperty] private bool removeSymlinksOnShutdown;
@ -110,6 +125,7 @@ public partial class SettingsViewModel : PageViewModelBase
SelectedTheme = settingsManager.Settings.Theme ?? AvailableThemes[1]; SelectedTheme = settingsManager.Settings.Theme ?? AvailableThemes[1];
RemoveSymlinksOnShutdown = settingsManager.Settings.RemoveFolderLinksOnShutdown; RemoveSymlinksOnShutdown = settingsManager.Settings.RemoveFolderLinksOnShutdown;
SelectedAnimationScale = settingsManager.Settings.AnimationScale;
settingsManager.RelayPropertyFor(this, settingsManager.RelayPropertyFor(this,
vm => vm.SelectedTheme, vm => vm.SelectedTheme,
@ -120,6 +136,9 @@ public partial class SettingsViewModel : PageViewModelBase
settings => settings.IsDiscordRichPresenceEnabled); settings => settings.IsDiscordRichPresenceEnabled);
DebugThrowAsyncExceptionCommand.WithNotificationErrorHandler(notificationService, LogLevel.Warn); DebugThrowAsyncExceptionCommand.WithNotificationErrorHandler(notificationService, LogLevel.Warn);
settingsManager.RelayPropertyFor(this,
vm => vm.SelectedAnimationScale,
settings => settings.AnimationScale);
} }
partial void OnSelectedThemeChanged(string? value) partial void OnSelectedThemeChanged(string? value)

2
StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml

@ -148,7 +148,7 @@
<StackPanel Margin="0,16,0,0" Orientation="Horizontal"> <StackPanel Margin="0,16,0,0" Orientation="Horizontal">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<Label Content="{Binding ReleaseLabelText, FallbackValue=Version}" /> <Label Content="{Binding ReleaseLabelText}" />
<ComboBox <ComboBox
ItemsSource="{Binding AvailableVersions}" ItemsSource="{Binding AvailableVersions}"
MinWidth="200" MinWidth="200"

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

@ -20,6 +20,7 @@ using FluentAvalonia.UI.Media;
using FluentAvalonia.UI.Media.Animation; using FluentAvalonia.UI.Media.Animation;
using FluentAvalonia.UI.Windowing; using FluentAvalonia.UI.Windowing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using StabilityMatrix.Avalonia.Animations;
using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels; using StabilityMatrix.Avalonia.ViewModels;
@ -141,7 +142,7 @@ public partial class MainWindow : AppWindowBase
{ {
throw new InvalidOperationException($"NavigationViewItem Tag must be of type ViewModelBase, not {nvi.Tag?.GetType()}"); throw new InvalidOperationException($"NavigationViewItem Tag must be of type ViewModelBase, not {nvi.Tag?.GetType()}");
} }
navigationService.NavigateTo(vm, e.RecommendedNavigationTransitionInfo); navigationService.NavigateTo(vm, new BetterEntranceNavigationTransition());
} }
} }

2
StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml

@ -162,7 +162,7 @@
Margin="8,8,8,0" Margin="8,8,8,0"
TextAlignment="Center" TextAlignment="Center"
Width="180" Width="180"
Text="{Binding Text, TargetNullValue=Updating so and so to the latest version... 5%}" Text="{Binding Text}"
TextWrapping="Wrap" TextWrapping="Wrap"
VerticalAlignment="Center" /> VerticalAlignment="Center" />
</Grid> </Grid>

128
StabilityMatrix.Avalonia/Views/SettingsPage.axaml

@ -14,46 +14,44 @@
<Grid> <Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto"> <ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid> <Grid RowDefinitions="Auto, Auto, Auto, Auto, Auto, Auto, Auto, Auto"
<StackPanel Spacing="2" Margin="12,16"> Margin="8, 16">
<!-- Theme --> <!-- Theme -->
<Grid RowDefinitions="auto,*"> <Grid Grid.Row="0" RowDefinitions="auto,*">
<TextBlock <TextBlock
FontWeight="Medium" FontWeight="Medium"
Text="Appearance" Text="Appearance"
Margin="0,0,0,8" /> Margin="0,0,0,8" />
<StackPanel Grid.Row="1">
<ui:SettingsExpander <ui:SettingsExpander
Grid.Row="1"
Header="Theme" Header="Theme"
IconSource="WeatherMoon" IconSource="WeatherMoon"
Margin="8,0"> Margin="8,0,8,4">
<ui:SettingsExpander.Footer> <ui:SettingsExpander.Footer>
<ComboBox <ComboBox
ItemsSource="{Binding AvailableThemes}" ItemsSource="{Binding AvailableThemes}"
SelectedItem="{Binding SelectedTheme}" SelectedItem="{Binding SelectedTheme}"
Margin="8"
MinWidth="100"/> MinWidth="100"/>
</ui:SettingsExpander.Footer> </ui:SettingsExpander.Footer>
</ui:SettingsExpander> </ui:SettingsExpander>
</StackPanel>
</Grid> </Grid>
<!-- TODO: Text2Image host port settings --> <!-- TODO: Text2Image host port settings -->
<!-- Checkpoints Manager Options --> <!-- Checkpoints Manager Options -->
<Grid RowDefinitions="auto,*,Auto"> <Grid Grid.Row="1" Margin="0,8,0,0" RowDefinitions="auto,*,Auto">
<TextBlock <TextBlock
FontWeight="Medium" FontWeight="Medium"
Text="Checkpoint Manager" Text="Checkpoint Manager"
Margin="0,16,0,8" /> Margin="0,0,0,8" />
<ui:SettingsExpander <ui:SettingsExpander
Grid.Row="1" Grid.Row="1"
IconSource="Folder" IconSource="Folder"
Header="Remove shared checkpoints directory symbolic links on shutdown" Header="Remove shared checkpoints directory symbolic links on shutdown"
Description="Select this option if you're having problems moving Stability Matrix to another drive" Description="Select this option if you're having problems moving Stability Matrix to another drive"
Margin="8,8"> Margin="8,0">
<ui:SettingsExpander.Footer> <ui:SettingsExpander.Footer>
<CheckBox Margin="4" <CheckBox Margin="8"
IsChecked="{Binding RemoveSymlinksOnShutdown}"/> IsChecked="{Binding RemoveSymlinksOnShutdown}"/>
</ui:SettingsExpander.Footer> </ui:SettingsExpander.Footer>
</ui:SettingsExpander> </ui:SettingsExpander>
@ -62,35 +60,34 @@
IconSource="Refresh" IconSource="Refresh"
Header="Reset Checkpoints Cache" Header="Reset Checkpoints Cache"
Description="Rebuilds the installed checkpoints cache. Use if checkpoints are incorrectly labeled in the Model Browser." Description="Rebuilds the installed checkpoints cache. Use if checkpoints are incorrectly labeled in the Model Browser."
Margin="8,0"> Margin="8, 4">
<ui:SettingsExpander.Footer> <ui:SettingsExpander.Footer>
<Button Margin="8" <Button Command="{Binding ResetCheckpointCache}"
Command="{Binding ResetCheckpointCache}"
Content="Reset Checkpoints Cache"/> Content="Reset Checkpoints Cache"/>
</ui:SettingsExpander.Footer> </ui:SettingsExpander.Footer>
</ui:SettingsExpander> </ui:SettingsExpander>
</Grid> </Grid>
<!-- Environment Options --> <!-- Environment Options -->
<Grid RowDefinitions="auto,*"> <Grid Grid.Row="2" Margin="0,8,0,0" RowDefinitions="Auto, Auto, Auto">
<TextBlock <TextBlock
FontWeight="Medium" FontWeight="Medium"
Text="Package Environment" Text="Package Environment"
Margin="0,16,0,0" /> Margin="0,0,0,8" />
<StackPanel Grid.Row="1" Margin="0,8" Spacing="4"> <ui:SettingsExpander Grid.Row="1"
<ui:SettingsExpander
Header="Environment Variables" Header="Environment Variables"
IconSource="OtherUser" IconSource="OtherUser"
Margin="8,0"> Margin="8,0">
<ui:SettingsExpander.Footer> <ui:SettingsExpander.Footer>
<Button Content="Edit" Command="{Binding OpenEnvVarsDialogCommand}"/> <Button Content="Edit"
Command="{Binding OpenEnvVarsDialogCommand}"/>
</ui:SettingsExpander.Footer> </ui:SettingsExpander.Footer>
</ui:SettingsExpander> </ui:SettingsExpander>
<ui:SettingsExpander <ui:SettingsExpander Grid.Row="2"
Header="Embedded Python" Header="Embedded Python"
Margin="8,0"> Margin="8,4">
<ui:SettingsExpander.IconSource> <ui:SettingsExpander.IconSource>
<controls:FASymbolIconSource Symbol="fa-brands fa-python"/> <controls:FASymbolIconSource Symbol="fa-brands fa-python"/>
</ui:SettingsExpander.IconSource> </ui:SettingsExpander.IconSource>
@ -105,44 +102,40 @@
</StackPanel> </StackPanel>
</ui:SettingsExpander.Footer> </ui:SettingsExpander.Footer>
</ui:SettingsExpander> </ui:SettingsExpander>
</StackPanel>
</Grid> </Grid>
<!-- Integrations --> <!-- Integrations -->
<Grid RowDefinitions="auto,*"> <Grid Grid.Row="3" Margin="0,8,0,0" RowDefinitions="auto,*">
<TextBlock <TextBlock
FontWeight="Medium" FontWeight="Medium"
Text="Integrations" Text="Integrations"
Margin="0,16,0,8" /> Margin="0,0,0,8" />
<StackPanel Grid.Row="1"> <ui:SettingsExpander Grid.Row="1"
<ui:SettingsExpander Header="Discord Rich Presence"
Header="Discord Rich Presence" Margin="8,0,8,4">
Margin="8,0"> <ui:SettingsExpander.IconSource>
<ui:SettingsExpander.IconSource> <controls:FASymbolIconSource Symbol="fa-brands fa-discord"/>
<controls:FASymbolIconSource Symbol="fa-brands fa-discord"/> </ui:SettingsExpander.IconSource>
</ui:SettingsExpander.IconSource> <ui:SettingsExpander.Footer>
<ui:SettingsExpander.Footer> <ToggleSwitch
<ToggleSwitch IsChecked="{Binding IsDiscordRichPresenceEnabled}" />
IsChecked="{Binding IsDiscordRichPresenceEnabled}" </ui:SettingsExpander.Footer>
Margin="4" /> </ui:SettingsExpander>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</StackPanel>
</Grid> </Grid>
<!-- System Options --> <!-- System Options -->
<Grid RowDefinitions="auto,*"> <Grid Grid.Row="4" Margin="0,8,0,0" RowDefinitions="auto,*">
<TextBlock <TextBlock
FontWeight="Medium" FontWeight="Medium"
Text="System" Text="System"
Margin="0,16,0,0" /> Margin="0,0,0,8" />
<ui:SettingsExpander <ui:SettingsExpander
Grid.Row="1" Grid.Row="1"
ToolTip.Tip="{OnPlatform Default='Only available on Windows', Windows={x:Null}}" ToolTip.Tip="{OnPlatform Default='Only available on Windows', Windows={x:Null}}"
Header="Add Stability Matrix to the Start Menu" Header="Add Stability Matrix to the Start Menu"
Description="Uses the current app location, you can run this again if you move the app" Description="Uses the current app location, you can run this again if you move the app"
IconSource="StarAdd" IconSource="StarAdd"
Margin="8"> Margin="8,0,8,4">
<ui:SettingsExpander.Footer> <ui:SettingsExpander.Footer>
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<controls:ProgressRing <controls:ProgressRing
@ -176,57 +169,84 @@
</Grid> </Grid>
<!-- Debug Options --> <!-- Debug Options -->
<Grid RowDefinitions="auto,*" <Grid Grid.Row="5" RowDefinitions="auto,*"
Margin="0,4,0,0" Margin="0,8,0,0"
IsVisible="{Binding SharedState.IsDebugMode}" > IsVisible="{Binding SharedState.IsDebugMode}" >
<TextBlock <TextBlock
FontWeight="Medium" FontWeight="Medium"
Text="Debug Options" Text="Debug Options"
Margin="0,16,0,0" /> Margin="0,0,0,8" />
<ui:SettingsExpander <ui:SettingsExpander
Grid.Row="1" Grid.Row="1"
IconSource="Code" IconSource="Code"
Command="{Binding LoadDebugInfo}" Command="{Binding LoadDebugInfo}"
Header="Debug Options" Header="Debug Options"
Margin="8"> Margin="8, 0,8,4">
<ui:SettingsExpanderItem Description="Paths" IconSource="Folder"> <ui:SettingsExpanderItem Description="Paths" IconSource="Folder"
Margin="4">
<SelectableTextBlock Text="{Binding DebugPaths}" <SelectableTextBlock Text="{Binding DebugPaths}"
Foreground="{DynamicResource TextControlPlaceholderForeground}" Foreground="{DynamicResource TextControlPlaceholderForeground}"
TextWrapping="WrapWithOverflow" /> TextWrapping="WrapWithOverflow" />
</ui:SettingsExpanderItem> </ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Description="Compat Info" IconSource="StarFilled"> <ui:SettingsExpanderItem Description="Compat Info" IconSource="StarFilled"
Margin="4,0,4,4">
<SelectableTextBlock Text="{Binding DebugCompatInfo}" <SelectableTextBlock Text="{Binding DebugCompatInfo}"
Foreground="{DynamicResource TextControlPlaceholderForeground}" Foreground="{DynamicResource TextControlPlaceholderForeground}"
TextWrapping="WrapWithOverflow" /> TextWrapping="WrapWithOverflow" />
</ui:SettingsExpanderItem> </ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Description="GPU Info" IconSource="FullScreenMaximize"> <ui:SettingsExpanderItem Description="GPU Info" IconSource="FullScreenMaximize"
Margin="4,0,4,4">
<SelectableTextBlock Text="{Binding DebugGpuInfo}" <SelectableTextBlock Text="{Binding DebugGpuInfo}"
Foreground="{DynamicResource TextControlPlaceholderForeground}" Foreground="{DynamicResource TextControlPlaceholderForeground}"
TextWrapping="WrapWithOverflow" /> TextWrapping="WrapWithOverflow" />
</ui:SettingsExpanderItem> </ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Notification" IconSource="CommentAdd"> <ui:SettingsExpanderItem Content="Animation Scale" IconSource="Clock"
Description="Lower values = faster animations. 0x means animations are instant."
Margin="4,0,4,4">
<ui:SettingsExpanderItem.Footer>
<ComboBox Margin="0, 8"
ItemsSource="{Binding AnimationScaleOptions}"
SelectedItem="{Binding SelectedAnimationScale}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding }"/><Run Text="x"/>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Notification" IconSource="CommentAdd"
Margin="4,0,4,4">
<ui:SettingsExpanderItem.Footer> <ui:SettingsExpanderItem.Footer>
<Button <Button
Margin="0, 8"
Command="{Binding DebugNotificationCommand}" Command="{Binding DebugNotificationCommand}"
Content="New Notification"/> Content="New Notification"/>
</ui:SettingsExpanderItem.Footer> </ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem> </ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Content Dialog" IconSource="NewWindow"> <ui:SettingsExpanderItem Content="Content Dialog" IconSource="NewWindow"
Margin="4,0,4,4">
<ui:SettingsExpanderItem.Footer> <ui:SettingsExpanderItem.Footer>
<Button <Button
Margin="0, 8"
Command="{Binding DebugContentDialogCommand}" Command="{Binding DebugContentDialogCommand}"
Content="Show Dialog"/> Content="Show Dialog"/>
</ui:SettingsExpanderItem.Footer> </ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem> </ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Exceptions" IconSource="Flag"> <ui:SettingsExpanderItem Content="Exceptions" IconSource="Flag"
Margin="4,0,4,4">
<ui:SettingsExpanderItem.Footer> <ui:SettingsExpanderItem.Footer>
<SplitButton <SplitButton
Margin="0, 8"
Command="{Binding DebugThrowExceptionCommand}" Command="{Binding DebugThrowExceptionCommand}"
Content="Command Exception"> Content="Command Exception">
@ -269,7 +289,7 @@
<!-- TODO: Directories card --> <!-- TODO: Directories card -->
<Grid RowDefinitions="auto,*" Margin="0,4,0,0"> <Grid Grid.Row="6" RowDefinitions="auto,*" Margin="0,4,0,0">
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
@ -316,9 +336,7 @@
</Grid> </Grid>
<!-- Extra space at the bottom --> <!-- Extra space at the bottom -->
<Panel Margin="0,0,0,16" /> <Panel Grid.Row="7" Margin="0,0,0,16" />
</StackPanel>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>

8
StabilityMatrix.Core/Helper/PrerequisiteHelper.cs

@ -56,14 +56,14 @@ public class PrerequisiteHelper : IPrerequisiteHelper
public async Task RunGit(string? workingDirectory = null, params string[] args) public async Task RunGit(string? workingDirectory = null, params string[] args)
{ {
var process = ProcessRunner.StartAnsiProcess(GitExePath, args, workingDirectory: workingDirectory); var process = ProcessRunner.StartAnsiProcess(GitExePath, args, workingDirectory: workingDirectory);
await ProcessRunner.WaitForExitConditionAsync(process); await ProcessRunner.WaitForExitConditionAsync(process).ConfigureAwait(false);
} }
public async Task<string> GetGitOutput(string? workingDirectory = null, params string[] args) public async Task<string> GetGitOutput(string? workingDirectory = null, params string[] args)
{ {
var process = await ProcessRunner.GetProcessOutputAsync(GitExePath, string.Join(" ", args), var output = await ProcessRunner.GetProcessOutputAsync(GitExePath, string.Join(" ", args),
workingDirectory: workingDirectory); workingDirectory: workingDirectory).ConfigureAwait(false);
return process; return output;
} }
public async Task InstallAllIfNecessary(IProgress<ProgressReport>? progress = null) public async Task InstallAllIfNecessary(IProgress<ProgressReport>? progress = null)

6
StabilityMatrix.Core/Models/PackageVersion.cs

@ -1,7 +1,7 @@
namespace StabilityMatrix.Core.Models; namespace StabilityMatrix.Core.Models;
public class PackageVersion public record PackageVersion
{ {
public string TagName { get; set; } public required string TagName { get; set; }
public string ReleaseNotesMarkdown { get; set; } public string? ReleaseNotesMarkdown { get; set; }
} }

20
StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs

@ -240,6 +240,7 @@ public abstract class BaseGitPackage : BasePackage
public override async Task<string> Update(InstalledPackage installedPackage, public override async Task<string> Update(InstalledPackage installedPackage,
IProgress<ProgressReport>? progress = null, bool includePrerelease = false) IProgress<ProgressReport>? progress = null, bool includePrerelease = false)
{ {
// Release mode
if (string.IsNullOrWhiteSpace(installedPackage.InstalledBranch)) if (string.IsNullOrWhiteSpace(installedPackage.InstalledBranch))
{ {
var releases = await GetAllReleases().ConfigureAwait(false); var releases = await GetAllReleases().ConfigureAwait(false);
@ -248,15 +249,20 @@ public abstract class BaseGitPackage : BasePackage
await InstallPackage(progress).ConfigureAwait(false); await InstallPackage(progress).ConfigureAwait(false);
return latestRelease.TagName; return latestRelease.TagName;
} }
else
// Commit mode
var allCommits = await GetAllCommits(
installedPackage.InstalledBranch).ConfigureAwait(false);
var latestCommit = allCommits?.First();
if (latestCommit is null || string.IsNullOrEmpty(latestCommit.Sha))
{ {
var allCommits = await GetAllCommits( throw new Exception("No commits found for branch");
installedPackage.InstalledBranch).ConfigureAwait(false);
var latestCommit = allCommits.First();
await DownloadPackage(latestCommit.Sha, true, progress);
await InstallPackage(progress).ConfigureAwait(false);
return latestCommit.Sha;
} }
await DownloadPackage(latestCommit.Sha, true, progress).ConfigureAwait(false);
await InstallPackage(progress).ConfigureAwait(false);
return latestCommit.Sha;
} }
// Send input to the running process. // Send input to the running process.

2
StabilityMatrix.Core/Models/Packages/ComfyUI.cs

@ -92,7 +92,7 @@ public class ComfyUI : BaseGitPackage
public override async Task<IEnumerable<PackageVersion>> GetAllVersions(bool isReleaseMode = true) public override async Task<IEnumerable<PackageVersion>> GetAllVersions(bool isReleaseMode = true)
{ {
var allBranches = await GetAllBranches(); var allBranches = await GetAllBranches().ConfigureAwait(false);
return allBranches.Select(b => new PackageVersion return allBranches.Select(b => new PackageVersion
{ {
TagName = $"{b.Name}", TagName = $"{b.Name}",

132
StabilityMatrix.Core/Models/Packages/InvokeAI.cs

@ -56,41 +56,10 @@ public class InvokeAI : BaseGitPackage
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders => new() public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders => new()
{ {
[SharedFolderType.StableDiffusion] = new[] [SharedFolderType.StableDiffusion] = new[] { RelativeRootPath + "/autoimport/main" },
{ [SharedFolderType.Lora] = new[] { RelativeRootPath + "/autoimport/lora" },
RelativeRootPath + "/models/sd-1/main", [SharedFolderType.TextualInversion] = new[] { RelativeRootPath + "/autoimport/embedding" },
RelativeRootPath + "/models/sd-2/main", [SharedFolderType.ControlNet] = new[] { RelativeRootPath + "/autoimport/controlnet" },
RelativeRootPath + "/models/sdxl/main",
RelativeRootPath + "/models/sdxl-refiner/main",
},
[SharedFolderType.Lora] = new[]
{
RelativeRootPath + "/models/sd-1/lora",
RelativeRootPath + "/models/sd-2/lora",
RelativeRootPath + "/models/sdxl/lora",
RelativeRootPath + "/models/sdxl-refiner/lora",
},
[SharedFolderType.TextualInversion] = new[]
{
RelativeRootPath + "/models/sd-1/embedding",
RelativeRootPath + "/models/sd-2/embedding",
RelativeRootPath + "/models/sdxl/embedding",
RelativeRootPath + "/models/sdxl-refiner/embedding",
},
[SharedFolderType.VAE] = new[]
{
RelativeRootPath + "/models/sd-1/vae",
RelativeRootPath + "/models/sd-2/vae",
RelativeRootPath + "/models/sdxl/vae",
RelativeRootPath + "/models/sdxl-refiner/vae",
},
[SharedFolderType.ControlNet] = new[]
{
RelativeRootPath + "/models/sd-1/controlnet",
RelativeRootPath + "/models/sd-2/controlnet",
RelativeRootPath + "/models/sdxl/controlnet",
RelativeRootPath + "/models/sdxl-refiner/controlnet",
},
}; };
// https://github.com/invoke-ai/InvokeAI/blob/main/docs/features/CONFIGURATION.md // https://github.com/invoke-ai/InvokeAI/blob/main/docs/features/CONFIGURATION.md
@ -216,6 +185,11 @@ public class InvokeAI : BaseGitPackage
{ {
progress?.Report(new ProgressReport(-1f, "Setting up venv", isIndeterminate: true)); progress?.Report(new ProgressReport(-1f, "Setting up venv", isIndeterminate: true));
if (installedPackage.FullPath is null)
{
throw new NullReferenceException("Installed package path is null");
}
await using var venvRunner = new PyVenvRunner(Path.Combine(installedPackage.FullPath, "venv")); await using var venvRunner = new PyVenvRunner(Path.Combine(installedPackage.FullPath, "venv"));
venvRunner.WorkingDirectory = installedPackage.FullPath; venvRunner.WorkingDirectory = installedPackage.FullPath;
venvRunner.EnvironmentVariables = GetEnvVars(installedPackage.FullPath); venvRunner.EnvironmentVariables = GetEnvVars(installedPackage.FullPath);
@ -355,92 +329,20 @@ public class InvokeAI : BaseGitPackage
public override Task SetupModelFolders(DirectoryPath installDirectory) public override Task SetupModelFolders(DirectoryPath installDirectory)
{ {
var modelsFolder = SettingsManager.ModelsDirectory; StabilityMatrix.Core.Helper.SharedFolders
if (!Directory.Exists(modelsFolder)) .SetupLinks(SharedFolders, SettingsManager.ModelsDirectory, installDirectory);
return Task.CompletedTask;
var connectedModelJsons =
Directory.GetFiles(modelsFolder, "*.cm-info.json", SearchOption.AllDirectories);
var connectedModelDict = new Dictionary<string, ConnectedModelInfo>();
foreach (var jsonFile in connectedModelJsons)
{
var json = File.ReadAllText(jsonFile);
var connectedModelInfo = JsonSerializer.Deserialize<ConnectedModelInfo>(json);
var extension = connectedModelInfo?.FileMetadata.Format switch
{
CivitModelFormat.SafeTensor => ".safetensors",
CivitModelFormat.PickleTensor => ".pt",
_ => string.Empty
};
if (string.IsNullOrWhiteSpace(extension) || connectedModelInfo == null)
continue;
var modelFilePath = jsonFile.Replace(".cm-info.json", extension);
if (File.Exists(modelFilePath))
{
connectedModelDict[modelFilePath] = connectedModelInfo;
}
}
foreach (var modelFilePath in connectedModelDict.Keys)
{
var model = connectedModelDict[modelFilePath];
var modelType = model.ModelType switch
{
CivitModelType.Checkpoint => "main",
CivitModelType.LORA => "lora",
CivitModelType.TextualInversion => "embedding",
CivitModelType.Controlnet => "controlnet",
_ => string.Empty
};
if (string.IsNullOrWhiteSpace(modelType))
continue;
var sourcePath = modelFilePath;
var destinationPath = Path.Combine(installDirectory, RelativeRootPath, "autoimport",
modelType, Path.GetFileName(modelFilePath));
try
{
File.CreateSymbolicLink(destinationPath, sourcePath);
}
catch (IOException e)
{
// File already exists
Logger.Warn(e,
$"Could not create symlink for {sourcePath} to {destinationPath} - file already exists");
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }
public override Task UpdateModelFolders(DirectoryPath installDirectory) => public override async Task UpdateModelFolders(DirectoryPath installDirectory)
SetupModelFolders(installDirectory); {
await StabilityMatrix.Core.Helper.SharedFolders.UpdateLinksForPackage(this,
SettingsManager.ModelsDirectory, installDirectory).ConfigureAwait(false);
}
public override Task RemoveModelFolderLinks(DirectoryPath installDirectory) public override Task RemoveModelFolderLinks(DirectoryPath installDirectory)
{ {
var autoImportDir = Path.Combine(installDirectory, RelativeRootPath, "autoimport"); StabilityMatrix.Core.Helper.SharedFolders.RemoveLinksForPackage(this, installDirectory);
var allSymlinks =
Directory.GetFiles(autoImportDir, "*.*", SearchOption.AllDirectories)
.Select(path => new FileInfo(path)).Where(file => file.LinkTarget != null);
foreach (var link in allSymlinks)
{
try
{
link.Delete();
}
catch (IOException e)
{
Logger.Warn(e, $"Could not delete symlink {link.FullName}");
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }

35
StabilityMatrix.Core/Models/Packages/VladAutomatic.cs

@ -303,45 +303,42 @@ public class VladAutomatic : BaseGitPackage
public override async Task<string> Update(InstalledPackage installedPackage, public override async Task<string> Update(InstalledPackage installedPackage,
IProgress<ProgressReport>? progress = null, bool includePrerelease = false) IProgress<ProgressReport>? progress = null, bool includePrerelease = false)
{ {
if (installedPackage.InstalledBranch is null)
{
throw new Exception("Installed branch is null");
}
progress?.Report(new ProgressReport(0.1f, message: "Downloading package update...", progress?.Report(new ProgressReport(0.1f, message: "Downloading package update...",
isIndeterminate: true, type: ProgressType.Download)); isIndeterminate: true, type: ProgressType.Download));
var version = await GithubApi.GetAllCommits(Author, Name, installedPackage.InstalledBranch); var version = await GithubApi.GetAllCommits(Author, Name, installedPackage.InstalledBranch).ConfigureAwait(false);
var latest = version?.FirstOrDefault(); var latest = version?.FirstOrDefault();
if (latest == null) if (latest?.Sha is null)
{ {
Logger.Warn("No latest version found for vlad"); throw new Exception("Could not get latest version");
return string.Empty;
} }
try try
{ {
var output = var output =
await PrerequisiteHelper.GetGitOutput(workingDirectory: installedPackage.FullPath, await PrerequisiteHelper
"rev-parse", "HEAD"); .GetGitOutput(installedPackage.FullPath, "rev-parse", "HEAD")
.ConfigureAwait(false);
if (output?.Replace("\n", "") == latest.Sha) if (output.Replace(Environment.NewLine, "") == latest.Sha)
{ {
return latest.Sha; return latest.Sha;
} }
} }
catch (Exception)
{
// ignored
}
try
{
await PrerequisiteHelper.RunGit(workingDirectory: installedPackage.FullPath, "pull",
"origin", installedPackage.InstalledBranch);
}
catch (Exception e) catch (Exception e)
{ {
Logger.Log(LogLevel.Error, e); Logger.Warn(e, "Could not get current git hash, continuing with update");
return string.Empty;
} }
await PrerequisiteHelper.RunGit(installedPackage.FullPath, "pull",
"origin", installedPackage.InstalledBranch).ConfigureAwait(false);
progress?.Report(new ProgressReport(1f, message: "Update Complete", isIndeterminate: false, progress?.Report(new ProgressReport(1f, message: "Update Complete", isIndeterminate: false,
type: ProgressType.Generic)); type: ProgressType.Generic));

2
StabilityMatrix.Core/Models/Settings/Settings.cs

@ -51,6 +51,8 @@ public class Settings
public HashSet<string>? InstalledModelHashes { get; set; } = new(); public HashSet<string>? InstalledModelHashes { get; set; } = new();
public float AnimationScale { get; set; } = 1.0f;
public void RemoveInstalledPackageAndUpdateActive(InstalledPackage package) public void RemoveInstalledPackageAndUpdateActive(InstalledPackage package)
{ {
RemoveInstalledPackageAndUpdateActive(package.Id); RemoveInstalledPackageAndUpdateActive(package.Id);

Loading…
Cancel
Save