diff --git a/CHANGELOG.md b/CHANGELOG.md
index f39913d8..d85a1427 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
## v2.5.0
+### Added
+- Added option to change the Shared Folder method for packages using the three-dots menu on the Packages page
### Changed
- Model Browser page size is now 20 instead of 14
### Fixed
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
index 5cbc24b5..cc38231d 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
+++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
@@ -1283,6 +1283,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Model Sharing.
+ ///
+ public static string Label_SharedModelStrategyShort {
+ get {
+ return ResourceManager.GetString("Label_SharedModelStrategyShort", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Show Model Images.
///
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx
index 88399d11..48e00684 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.resx
+++ b/StabilityMatrix.Avalonia/Languages/Resources.resx
@@ -642,4 +642,7 @@
Automatically scroll to end of console output
+
+ Model Sharing
+
\ No newline at end of file
diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs
index 27b24581..89cc5566 100644
--- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
@@ -20,7 +21,6 @@ using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Packages;
-using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Services;
@@ -50,6 +50,18 @@ public partial class PackageCardViewModel : ProgressViewModel
[ObservableProperty]
private bool isUnknownPackage;
+ [ObservableProperty]
+ private bool isSharedModelSymlink;
+
+ [ObservableProperty]
+ private bool isSharedModelConfig;
+
+ [ObservableProperty]
+ private bool isSharedModelDisabled;
+
+ [ObservableProperty]
+ private bool canUseConfigMethod;
+
public PackageCardViewModel(
ILogger logger,
IPackageFactory packageFactory,
@@ -85,11 +97,18 @@ public partial class PackageCardViewModel : ProgressViewModel
var basePackage = packageFactory[value.PackageName];
CardImageSource = basePackage?.PreviewImageUri.ToString() ?? Assets.NoImage.ToString();
InstalledVersion = value.Version?.DisplayVersion ?? "Unknown";
+ CanUseConfigMethod =
+ basePackage?.AvailableSharedFolderMethods.Contains(SharedFolderMethod.Configuration)
+ ?? false;
}
}
public override async Task OnLoadedAsync()
{
+ IsSharedModelSymlink = Package?.PreferredSharedFolderMethod == SharedFolderMethod.Symlink;
+ IsSharedModelConfig =
+ Package?.PreferredSharedFolderMethod == SharedFolderMethod.Configuration;
+ IsSharedModelDisabled = Package?.PreferredSharedFolderMethod == SharedFolderMethod.None;
IsUpdateAvailable = await HasUpdate();
}
@@ -312,4 +331,78 @@ public partial class PackageCardViewModel : ProgressViewModel
return false;
}
}
+
+ public void ToggleSharedModelSymlink() => IsSharedModelSymlink = !IsSharedModelSymlink;
+
+ public void ToggleSharedModelConfig() => IsSharedModelConfig = !IsSharedModelConfig;
+
+ public void ToggleSharedModelNone() => IsSharedModelDisabled = !IsSharedModelDisabled;
+
+ // fake radio button stuff
+ partial void OnIsSharedModelSymlinkChanged(bool oldValue, bool newValue)
+ {
+ if (oldValue == newValue)
+ return;
+
+ var basePackage = packageFactory[Package!.PackageName];
+ if (!newValue)
+ {
+ basePackage!.RemoveModelFolderLinks(Package.FullPath!, SharedFolderMethod.Symlink);
+ return;
+ }
+
+ basePackage!.SetupModelFolders(Package.FullPath!, SharedFolderMethod.Symlink);
+ settingsManager.Transaction(
+ s =>
+ s.InstalledPackages.First(x => x.Id == Package.Id).PreferredSharedFolderMethod =
+ SharedFolderMethod.Symlink
+ );
+
+ IsSharedModelConfig = false;
+ IsSharedModelDisabled = false;
+ }
+
+ partial void OnIsSharedModelConfigChanged(bool oldValue, bool newValue)
+ {
+ if (oldValue == newValue)
+ return;
+
+ var basePackage = packageFactory[Package!.PackageName];
+ if (!newValue)
+ {
+ basePackage!.RemoveModelFolderLinks(
+ Package.FullPath!,
+ SharedFolderMethod.Configuration
+ );
+ return;
+ }
+
+ basePackage!.SetupModelFolders(Package.FullPath!, SharedFolderMethod.Configuration);
+ settingsManager.Transaction(
+ s =>
+ s.InstalledPackages.First(x => x.Id == Package.Id).PreferredSharedFolderMethod =
+ SharedFolderMethod.Configuration
+ );
+
+ IsSharedModelSymlink = false;
+ IsSharedModelDisabled = false;
+ }
+
+ partial void OnIsSharedModelDisabledChanged(bool oldValue, bool newValue)
+ {
+ if (oldValue == newValue)
+ return;
+
+ if (!newValue)
+ return;
+
+ settingsManager.Transaction(
+ s =>
+ s.InstalledPackages.First(x => x.Id == Package!.Id).PreferredSharedFolderMethod =
+ SharedFolderMethod.None
+ );
+
+ IsSharedModelSymlink = false;
+ IsSharedModelConfig = false;
+ }
}
diff --git a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml
index 53862ecc..d2d60e39 100644
--- a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml
+++ b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml
@@ -9,6 +9,7 @@
xmlns:packageManager="clr-namespace:StabilityMatrix.Avalonia.ViewModels.PackageManager"
xmlns:faicon="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
+ xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="viewModels:PackageManagerViewModel"
x:CompileBindings="True"
@@ -17,124 +18,186 @@
-
-
+ FontSize="24" />
+
-
-
-
-
-
-
-
+ ItemsSource="{Binding PackageCards}">
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+ VerticalAlignment="Stretch"
+ IsVisible="{Binding IsProgressVisible}" />
+
+
+
+
-
-
-
-
-
+
+
+
+
-
+
-
+ IsOpen="{Binding !Packages.Count}" />
+
Task.CompletedTask,
+ SharedFolderMethod.Configuration => RemoveConfigSection(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
SharedFolderMethod.Symlink
=> base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod),
@@ -348,6 +348,42 @@ public class ComfyUI : BaseGitPackage
};
}
+ private Task RemoveConfigSection(string installDirectory)
+ {
+ var extraPathsYamlPath = Path.Combine(installDirectory, "extra_model_paths.yaml");
+ var exists = File.Exists(extraPathsYamlPath);
+ if (!exists)
+ {
+ return Task.CompletedTask;
+ }
+
+ var yaml = File.ReadAllText(extraPathsYamlPath);
+ using var sr = new StringReader(yaml);
+ var yamlStream = new YamlStream();
+ yamlStream.Load(sr);
+
+ if (!yamlStream.Documents.Any())
+ {
+ return Task.CompletedTask;
+ }
+
+ var root = yamlStream.Documents[0].RootNode;
+ if (root is not YamlMappingNode mappingNode)
+ {
+ return Task.CompletedTask;
+ }
+
+ mappingNode.Children.Remove("stability_matrix");
+
+ var serializer = new SerializerBuilder()
+ .WithNamingConvention(UnderscoredNamingConvention.Instance)
+ .Build();
+ var yamlData = serializer.Serialize(mappingNode);
+ File.WriteAllText(extraPathsYamlPath, yamlData);
+
+ return Task.CompletedTask;
+ }
+
private async Task InstallRocmTorch(
PyVenvRunner venvRunner,
IProgress? progress = null,
diff --git a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
index b7e13daa..179784d4 100644
--- a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
+++ b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
@@ -444,6 +444,61 @@ public class VladAutomatic : BaseGitPackage
SharedFolderMethod.Symlink
=> base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod),
SharedFolderMethod.None => Task.CompletedTask,
+ SharedFolderMethod.Configuration => RemoveConfigSettings(installDirectory),
_ => Task.CompletedTask
};
+
+ private Task RemoveConfigSettings(string installDirectory)
+ {
+ var configJsonPath = Path.Combine(installDirectory, "config.json");
+ var exists = File.Exists(configJsonPath);
+ JsonObject? configRoot;
+ if (exists)
+ {
+ var configJson = File.ReadAllText(configJsonPath);
+ try
+ {
+ configRoot = JsonSerializer.Deserialize(configJson);
+ if (configRoot == null)
+ {
+ return Task.CompletedTask;
+ }
+ }
+ catch (JsonException e)
+ {
+ Logger.Error(e, "Error removing Vlad shared model config");
+ return Task.CompletedTask;
+ }
+ }
+ else
+ {
+ return Task.CompletedTask;
+ }
+
+ configRoot.Remove("ckpt_dir");
+ configRoot.Remove("diffusers_dir");
+ configRoot.Remove("vae_dir");
+ configRoot.Remove("lora_dir");
+ configRoot.Remove("lyco_dir");
+ configRoot.Remove("embeddings_dir");
+ configRoot.Remove("hypernetwork_dir");
+ configRoot.Remove("codeformer_models_path");
+ configRoot.Remove("gfpgan_models_path");
+ configRoot.Remove("bsrgan_models_path");
+ configRoot.Remove("esrgan_models_path");
+ configRoot.Remove("realesrgan_models_path");
+ configRoot.Remove("scunet_models_path");
+ configRoot.Remove("swinir_models_path");
+ configRoot.Remove("ldsr_models_path");
+ configRoot.Remove("clip_models_path");
+ configRoot.Remove("control_net_models_path");
+
+ var configJsonStr = JsonSerializer.Serialize(
+ configRoot,
+ new JsonSerializerOptions { WriteIndented = true }
+ );
+ File.WriteAllText(configJsonPath, configJsonStr);
+
+ return Task.CompletedTask;
+ }
}