From b4fd64e7fc9592b60ba9dd2c6fa3887a1986080e Mon Sep 17 00:00:00 2001 From: jt Date: Sat, 18 Nov 2023 00:14:22 -0800 Subject: [PATCH 001/166] macos fixes & remove mac label for volta --- .../StabilityMatrix.Avalonia.Diagnostics.csproj | 1 + StabilityMatrix.Avalonia/App.axaml | 1 + StabilityMatrix.Avalonia/Helpers/UriHandler.cs | 2 +- StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj | 9 +++++++++ StabilityMatrix.Core/Models/Packages/BasePackage.cs | 5 +++++ StabilityMatrix.Core/Models/Packages/VoltaML.cs | 2 +- StabilityMatrix.Core/Processes/ProcessRunner.cs | 2 +- StabilityMatrix.Core/StabilityMatrix.Core.csproj | 1 + StabilityMatrix.Tests/StabilityMatrix.Tests.csproj | 1 + StabilityMatrix.UITests/StabilityMatrix.UITests.csproj | 1 + 10 files changed, 22 insertions(+), 3 deletions(-) diff --git a/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj b/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj index eb84e3f3..84e8c3f9 100644 --- a/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj +++ b/StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj @@ -21,6 +21,7 @@ + diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml index c2821641..2e08109c 100644 --- a/StabilityMatrix.Avalonia/App.axaml +++ b/StabilityMatrix.Avalonia/App.axaml @@ -4,6 +4,7 @@ xmlns:local="using:StabilityMatrix.Avalonia" xmlns:idcr="using:Dock.Avalonia.Controls.Recycling" xmlns:styling="clr-namespace:FluentAvalonia.Styling;assembly=FluentAvalonia" + Name="Stability Matrix" RequestedThemeVariant="Dark"> diff --git a/StabilityMatrix.Avalonia/Helpers/UriHandler.cs b/StabilityMatrix.Avalonia/Helpers/UriHandler.cs index e500a1e4..a6f04b3d 100644 --- a/StabilityMatrix.Avalonia/Helpers/UriHandler.cs +++ b/StabilityMatrix.Avalonia/Helpers/UriHandler.cs @@ -64,7 +64,7 @@ public class UriHandler { RegisterUriSchemeWin(); } - else + else if (Compat.IsLinux) { RegisterUriSchemeUnix(); } diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index fb16d35f..d3111ecd 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -1,5 +1,6 @@  + Stability Matrix WinExe net8.0 win-x64;linux-x64;osx-x64;osx-arm64 @@ -14,6 +15,13 @@ true + + + StabilityMatrix.URL + stabilitymatrix;stabilitymatrix:// + + + @@ -37,6 +45,7 @@ + diff --git a/StabilityMatrix.Core/Models/Packages/BasePackage.cs b/StabilityMatrix.Core/Models/Packages/BasePackage.cs index 29ff06b9..f704b0f0 100644 --- a/StabilityMatrix.Core/Models/Packages/BasePackage.cs +++ b/StabilityMatrix.Core/Models/Packages/BasePackage.cs @@ -133,6 +133,11 @@ public abstract class BasePackage return TorchVersion.DirectMl; } + if (Compat.IsMacOS && Compat.IsArm && AvailableTorchVersions.Contains(TorchVersion.Mps)) + { + return TorchVersion.Mps; + } + return TorchVersion.Cpu; } diff --git a/StabilityMatrix.Core/Models/Packages/VoltaML.cs b/StabilityMatrix.Core/Models/Packages/VoltaML.cs index eeef1ce3..933e4fb3 100644 --- a/StabilityMatrix.Core/Models/Packages/VoltaML.cs +++ b/StabilityMatrix.Core/Models/Packages/VoltaML.cs @@ -62,7 +62,7 @@ public class VoltaML : BaseGitPackage public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Symlink; public override IEnumerable AvailableTorchVersions => - new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Mps }; + new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl }; public override IEnumerable AvailableSharedFolderMethods => new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None }; diff --git a/StabilityMatrix.Core/Processes/ProcessRunner.cs b/StabilityMatrix.Core/Processes/ProcessRunner.cs index 3a20cc60..8adde5eb 100644 --- a/StabilityMatrix.Core/Processes/ProcessRunner.cs +++ b/StabilityMatrix.Core/Processes/ProcessRunner.cs @@ -92,7 +92,7 @@ public static class ProcessRunner else if (Compat.IsMacOS) { using var process = new Process(); - process.StartInfo.FileName = "explorer"; + process.StartInfo.FileName = "open"; process.StartInfo.Arguments = $"-R {Quote(filePath)}"; process.Start(); await process.WaitForExitAsync().ConfigureAwait(false); diff --git a/StabilityMatrix.Core/StabilityMatrix.Core.csproj b/StabilityMatrix.Core/StabilityMatrix.Core.csproj index 254f8efe..f62ba22a 100644 --- a/StabilityMatrix.Core/StabilityMatrix.Core.csproj +++ b/StabilityMatrix.Core/StabilityMatrix.Core.csproj @@ -23,6 +23,7 @@ + diff --git a/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj b/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj index 87423475..a0acdc33 100644 --- a/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj +++ b/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj index 8062d3bd..961598f0 100644 --- a/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj +++ b/StabilityMatrix.UITests/StabilityMatrix.UITests.csproj @@ -11,6 +11,7 @@ + From 7aa13bbd25a82b978bdae96427cc49f614dc3dd9 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 23 Nov 2023 18:44:16 -0500 Subject: [PATCH 002/166] Update release.yml --- .github/workflows/release.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 44a9426f..26f06615 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -209,8 +209,6 @@ jobs: files: | StabilityMatrix-win-x64.zip StabilityMatrix-linux-x64.zip - StabilityMatrix-win-x64/StabilityMatrix.exe - StabilityMatrix-linux-x64/StabilityMatrix.AppImage fail_on_unmatched_files: true tag_name: v${{ github.event.inputs.version }} body: ${{ steps.release_notes.outputs.release_notes }} @@ -235,10 +233,10 @@ jobs: python-version: '3.11' - name: Install Python Dependencies - run: pip install stability-matrix-tools~=0.2.7 + run: pip install stability-matrix-tools>=0.2.15 - name: Publish Auto-Update Release - run: sm-tools updates publish-matrix -v $RELEASE_VERSION -y + run: sm-tools updates publish-matrix-v3 -v ${{ github.event.inputs.version }} -y publish-auto-update-b2: name: Publish Auto-Update Release (B2) From d0121ca026b8f1fd472eaa81e94dba090281cc21 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 26 Nov 2023 17:27:02 -0800 Subject: [PATCH 003/166] Added HuggingFace page for HF model downloads (still WIP) & updated Packages to use the new .net 8 constructor feature thing --- StabilityMatrix.Avalonia/App.axaml.cs | 3 +- StabilityMatrix.Avalonia/Assets.cs | 3 + .../Assets/hf-packages.json | 335 ++++++++++++++++++ .../Controls/StackExpander.axaml | 2 + .../Controls/StackExpander.axaml.cs | 4 +- .../DesignData/DesignData.cs | 3 + .../Languages/Resources.Designer.cs | 9 + .../Languages/Resources.resx | 3 + .../HuggingFace/HuggingFaceModelType.cs | 38 ++ .../Models/HuggingFace/HuggingfaceItem.cs | 12 + .../StabilityMatrix.Avalonia.csproj | 1 + .../HuggingFacePage/CategoryViewModel.cs | 53 +++ .../HuggingfaceItemViewModel.cs | 25 ++ .../ViewModels/HuggingFacePageViewModel.cs | 192 ++++++++++ .../Views/HuggingFacePage.axaml | 184 ++++++++++ .../Views/HuggingFacePage.axaml.cs | 13 + .../Models/Packages/A3WebUI.cs | 187 +++++----- .../Models/Packages/ComfyUI.cs | 197 +++++----- .../Models/Packages/FocusControlNet.cs | 15 +- .../Models/Packages/Fooocus.cs | 15 +- .../Models/Packages/FooocusMre.cs | 15 +- .../Models/Packages/InvokeAI.cs | 109 +++--- .../Models/Packages/KohyaSs.cs | 135 ++++--- .../Models/Packages/RuinedFooocus.cs | 20 +- .../Packages/StableDiffusionDirectMl.cs | 15 +- .../Models/Packages/StableDiffusionUx.cs | 183 +++++----- .../Models/Packages/VladAutomatic.cs | 173 ++++----- .../Models/Packages/VoltaML.cs | 15 +- .../Models/SharedFolderType.cs | 4 +- 29 files changed, 1428 insertions(+), 535 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Assets/hf-packages.json create mode 100644 StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs create mode 100644 StabilityMatrix.Avalonia/Models/HuggingFace/HuggingfaceItem.cs create mode 100644 StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/CategoryViewModel.cs create mode 100644 StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/HuggingfaceItemViewModel.cs create mode 100644 StabilityMatrix.Avalonia/ViewModels/HuggingFacePageViewModel.cs create mode 100644 StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml create mode 100644 StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml.cs diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index 36098533..5371ca1f 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -291,7 +291,8 @@ public sealed class App : Application provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), - provider.GetRequiredService() + provider.GetRequiredService(), + provider.GetRequiredService() }, FooterPages = { provider.GetRequiredService() } } diff --git a/StabilityMatrix.Avalonia/Assets.cs b/StabilityMatrix.Avalonia/Assets.cs index 6626d903..40d7153c 100644 --- a/StabilityMatrix.Avalonia/Assets.cs +++ b/StabilityMatrix.Avalonia/Assets.cs @@ -32,6 +32,9 @@ internal static class Assets public static AvaloniaResource ThemeMatrixDarkJson => new("avares://StabilityMatrix.Avalonia/Assets/ThemeMatrixDark.json"); + public static AvaloniaResource HfPackagesJson => + new("avares://StabilityMatrix.Avalonia/Assets/hf-packages.json"); + private const UnixFileMode Unix755 = UnixFileMode.UserRead | UnixFileMode.UserWrite diff --git a/StabilityMatrix.Avalonia/Assets/hf-packages.json b/StabilityMatrix.Avalonia/Assets/hf-packages.json new file mode 100644 index 00000000..9b19edb3 --- /dev/null +++ b/StabilityMatrix.Avalonia/Assets/hf-packages.json @@ -0,0 +1,335 @@ +[ + { + "ModelCategory": "BaseModel", + "ModelName": "Stable Diffusion 1.5", + "RepositoryPath": "runwayml/stable-diffusion-v1-5", + "Files": [ + "v1-5-pruned-emaonly.safetensors" + ], + "LicenseType": "CreativeML Open RAIL-M" + }, + { + "ModelCategory": "BaseModel", + "ModelName": "Stable Diffusion 2.1", + "RepositoryPath": "stabilityai/stable-diffusion-2-1", + "Files": [ + "v2-1_768-ema-pruned.safetensors" + ], + "LicenseType": "Open RAIL++" + }, + { + "ModelCategory": "BaseModel", + "ModelName": "Stable Diffusion XL (Base)", + "RepositoryPath": "stabilityai/stable-diffusion-xl-base-1.0", + "Files": [ + "sd_xl_base_1.0_0.9vae.safetensors", + "sd_xl_offset_example-lora_1.0.safetensors" + ], + "LicenseType": "Open RAIL++", + "LicensePath": "LICENSE.md" + }, + { + "ModelCategory": "BaseModel", + "ModelName": "Stable Diffusion XL (Refiner)", + "RepositoryPath": "stabilityai/stable-diffusion-xl-refiner-1.0", + "Files": [ + "sd_xl_refiner_1.0_0.9vae.safetensors" + ], + "LicenseType": "Open RAIL++", + "LicensePath": "LICENSE.md" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Canny", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_canny.pth", + "control_v11p_sd15_canny.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Depth", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11f1p_sd15_depth.pth", + "control_v11f1p_sd15_depth.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "MLSD", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_mlsd.pth", + "control_v11p_sd15_mlsd.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Inpaint", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_inpaint.pth", + "control_v11p_sd15_inpaint.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "IP2P", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11e_sd15_ip2p.pth", + "control_v11e_sd15_ip2p.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Tile", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11f1e_sd15_tile.pth", + "control_v11f1e_sd15_tile.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "OpenPose", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_openpose.pth", + "control_v11p_sd15_openpose.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "LineArt", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_lineart.pth", + "control_v11p_sd15_lineart.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "LineArt Anime", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15s2_lineart_anime.pth", + "control_v11p_sd15s2_lineart_anime.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "NormalBae", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_normalbae.pth", + "control_v11p_sd15_normalbae.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Seg", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_seg.pth", + "control_v11p_sd15_seg.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Soft Edge", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_softedge.pth", + "control_v11p_sd15_softedge.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Scribble", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11p_sd15_scribble.pth", + "control_v11p_sd15_scribble.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "ControlNet", + "ModelName": "Shuffle", + "RepositoryPath": "lllyasviel/ControlNet-v1-1", + "Files": [ + "control_v11e_sd15_shuffle.pth", + "control_v11e_sd15_shuffle.yaml" + ], + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Canny", + "RepositoryPath": "lllyasviel/control_v11p_sd15_canny", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "canny", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Depth", + "RepositoryPath": "lllyasviel/control_v11f1p_sd15_depth", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "depth", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "MLSD", + "RepositoryPath": "lllyasviel/control_v11p_sd15_mlsd", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "MLSD", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Inpaint", + "RepositoryPath": "lllyasviel/control_v11p_sd15_inpaint", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "inpaint", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "IP2P", + "RepositoryPath": "lllyasviel/control_v11e_sd15_ip2p", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "ip2p", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Tile", + "RepositoryPath": "lllyasviel/control_v11f1e_sd15_tile", + "Files": [ + "diffusion_pytorch_model.bin", + "config.json" + ], + "Subfolder": "tile", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "OpenPose", + "RepositoryPath": "lllyasviel/control_v11p_sd15_openpose", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "openpose", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "LineArt", + "RepositoryPath": "lllyasviel/control_v11p_sd15_lineart", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "lineart", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "LineArt Anime", + "RepositoryPath": "lllyasviel/control_v11p_sd15s2_lineart_anime", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "lineart_anime", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "NormalBae", + "RepositoryPath": "lllyasviel/control_v11p_sd15_normalbae", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "normalbae", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Seg", + "RepositoryPath": "lllyasviel/control_v11p_sd15_seg", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "seg", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Soft Edge", + "RepositoryPath": "lllyasviel/control_v11p_sd15_softedge", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "softedge", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Scribble", + "RepositoryPath": "lllyasviel/control_v11p_sd15_scribble", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "scribble", + "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersControlNet", + "ModelName": "Shuffle", + "RepositoryPath": "lllyasviel/control_v11e_sd15_shuffle", + "Files": [ + "diffusion_pytorch_model.safetensors", + "config.json" + ], + "Subfolder": "shuffle", + "LicenseType": "Open RAIL" + } +] diff --git a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml index b83b553c..dbec6300 100644 --- a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml +++ b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml @@ -21,6 +21,8 @@ diff --git a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml.cs b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml.cs index 38208891..e2479b73 100644 --- a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml.cs +++ b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml.cs @@ -1,11 +1,11 @@ using Avalonia; -using Avalonia.Controls.Primitives; +using Avalonia.Controls; using StabilityMatrix.Core.Attributes; namespace StabilityMatrix.Avalonia.Controls; [Transient] -public class StackExpander : TemplatedControl +public class StackExpander : Expander { public static readonly StyledProperty SpacingProperty = AvaloniaProperty.Register< StackCard, diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 09f3a993..53d01d6e 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -426,6 +426,9 @@ public static class DesignData public static LaunchPageViewModel LaunchPageViewModel => Services.GetRequiredService(); + + public static HuggingFacePageViewModel HuggingFacePageViewModel => + Services.GetRequiredService(); public static OutputsPageViewModel OutputsPageViewModel { diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index efb0216f..f41d4407 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -338,6 +338,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Open on Hugging Face. + /// + public static string Action_OpenOnHuggingFace { + get { + return ResourceManager.GetString("Action_OpenOnHuggingFace", resourceCulture); + } + } + /// /// Looks up a localized string similar to Open Project.... /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index 76da7ad6..fa76512d 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -813,4 +813,7 @@ Additional folders such as IPAdapters and TextualInversions (embeddings) can be enabled here + + Open on Hugging Face + diff --git a/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs b/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs new file mode 100644 index 00000000..c2869e72 --- /dev/null +++ b/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs @@ -0,0 +1,38 @@ +using System.Text.Json.Serialization; +using StabilityMatrix.Core.Converters.Json; +using StabilityMatrix.Core.Extensions; +using StabilityMatrix.Core.Models; + +namespace StabilityMatrix.Avalonia.Models.HuggingFace; + +[JsonConverter(typeof(DefaultUnknownEnumConverter))] +public enum HuggingFaceModelType +{ + [Description("Base Models")] + [ConvertTo(SharedFolderType.StableDiffusion)] + BaseModel, + + [Description("ControlNets")] + [ConvertTo(SharedFolderType.ControlNet)] + ControlNet, + + [Description("ControlNets (Diffusers)")] + [ConvertTo(SharedFolderType.ControlNet)] + DiffusersControlNet, + + [Description("IP Adapters")] + [ConvertTo(SharedFolderType.IpAdapter)] + IpAdapter, + + [Description("IP Adapters (Diffusers)")] + [ConvertTo(SharedFolderType.IpAdapter)] + DiffusersIpAdapter, + + [Description("T2I Adapters")] + [ConvertTo(SharedFolderType.T2IAdapter)] + T2IAdapter, + + [Description("T2I Adapters (Diffusers)")] + [ConvertTo(SharedFolderType.T2IAdapter)] + DiffusersT2IAdapter, +} diff --git a/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingfaceItem.cs b/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingfaceItem.cs new file mode 100644 index 00000000..95e02905 --- /dev/null +++ b/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingfaceItem.cs @@ -0,0 +1,12 @@ +namespace StabilityMatrix.Avalonia.Models.HuggingFace; + +public class HuggingfaceItem +{ + public required HuggingFaceModelType ModelCategory { get; set; } + public required string ModelName { get; set; } + public required string RepositoryPath { get; set; } + public required string[] Files { get; set; } + public required string LicenseType { get; set; } + public string? LicensePath { get; set; } + public string? Subfolder { get; set; } +} diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index e612293a..96e41d22 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -79,6 +79,7 @@ + diff --git a/StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/CategoryViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/CategoryViewModel.cs new file mode 100644 index 00000000..ecc9321c --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/CategoryViewModel.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using DynamicData; +using DynamicData.Binding; +using StabilityMatrix.Avalonia.Models.HuggingFace; +using StabilityMatrix.Avalonia.ViewModels.Base; + +namespace StabilityMatrix.Avalonia.ViewModels.HuggingFacePage; + +public partial class CategoryViewModel : ViewModelBase +{ + [ObservableProperty] + private IObservableCollection items = + new ObservableCollectionExtended(); + + public SourceCache ItemsCache { get; } = + new(i => i.RepositoryPath + i.ModelName); + + [ObservableProperty] + private string? title; + + [ObservableProperty] + private bool isChecked; + + [ObservableProperty] + private int numSelected; + + public CategoryViewModel(IEnumerable items) + { + ItemsCache + .Connect() + .DeferUntilLoaded() + .Transform(i => new HuggingfaceItemViewModel { Item = i }) + .Bind(Items) + .WhenPropertyChanged(p => p.IsSelected) + .Subscribe(_ => NumSelected = Items.Count(i => i.IsSelected)); + + ItemsCache.EditDiff(items, (a, b) => a.RepositoryPath == b.RepositoryPath); + } + + partial void OnIsCheckedChanged(bool value) + { + if (Items is null) + return; + + foreach (var item in Items) + { + item.IsSelected = value; + } + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/HuggingfaceItemViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/HuggingfaceItemViewModel.cs new file mode 100644 index 00000000..2fe59d6a --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/HuggingfaceItemViewModel.cs @@ -0,0 +1,25 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using StabilityMatrix.Avalonia.Models.HuggingFace; +using StabilityMatrix.Avalonia.ViewModels.Base; + +namespace StabilityMatrix.Avalonia.ViewModels.HuggingFacePage; + +public partial class HuggingfaceItemViewModel : ViewModelBase +{ + [ObservableProperty] + private HuggingfaceItem item; + + [ObservableProperty] + private bool isSelected; + + public string LicenseUrl => + $"https://huggingface.co/{Item.RepositoryPath}/blob/main/{Item.LicensePath ?? "README.md"}"; + public string RepoUrl => $"https://huggingface.co/{Item.RepositoryPath}"; + + [RelayCommand] + private void ToggleSelected() + { + IsSelected = !IsSelected; + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/HuggingFacePageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/HuggingFacePageViewModel.cs new file mode 100644 index 00000000..406b1d62 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/HuggingFacePageViewModel.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Avalonia.Controls.Notifications; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using DynamicData; +using DynamicData.Binding; +using FluentAvalonia.UI.Controls; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Models.HuggingFace; +using StabilityMatrix.Avalonia.Services; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.ViewModels.HuggingFacePage; +using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Extensions; +using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.FileInterfaces; +using StabilityMatrix.Core.Models.Progress; +using StabilityMatrix.Core.Services; + +namespace StabilityMatrix.Avalonia.ViewModels; + +[View(typeof(Views.HuggingFacePage))] +[Singleton] +public partial class HuggingFacePageViewModel : PageViewModelBase +{ + private readonly ITrackedDownloadService trackedDownloadService; + private readonly ISettingsManager settingsManager; + private readonly INotificationService notificationService; + public override string Title => "Hugging Face"; + public override IconSource IconSource => + new FASymbolIconSource { Symbol = "fa-solid fa-file-code" }; + + public SourceCache ItemsCache { get; } = + new(i => i.RepositoryPath + i.ModelName); + + public IObservableCollection Categories { get; set; } = + new ObservableCollectionExtended(); + + public string DownloadPercentText => + Math.Abs(TotalProgress.Percentage - 100f) < 0.001f + ? "Download Complete" + : $"Downloading {TotalProgress.Percentage:0}%"; + + [ObservableProperty] + private int numSelected; + + private ConcurrentDictionary progressReports = new(); + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(DownloadPercentText))] + private ProgressReport totalProgress; + + private readonly DispatcherTimer progressTimer = + new() { Interval = TimeSpan.FromMilliseconds(100) }; + + public HuggingFacePageViewModel( + ITrackedDownloadService trackedDownloadService, + ISettingsManager settingsManager, + INotificationService notificationService + ) + { + this.trackedDownloadService = trackedDownloadService; + this.settingsManager = settingsManager; + this.notificationService = notificationService; + + ItemsCache + .Connect() + .DeferUntilLoaded() + .Group(i => i.ModelCategory) + .Transform( + g => + new CategoryViewModel(g.Cache.Items) + { + Title = g.Key.GetDescription() ?? g.Key.ToString() + } + ) + .SortBy(vm => vm.Title) + .Bind(Categories) + .WhenAnyPropertyChanged() + .Subscribe(_ => NumSelected = Categories.Sum(c => c.NumSelected)); + + progressTimer.Tick += (_, _) => + { + var currentSum = 0ul; + var totalSum = 0ul; + foreach (var progress in progressReports.Values) + { + currentSum += progress.Current ?? 0; + totalSum += progress.Total ?? 0; + } + + TotalProgress = new ProgressReport(current: currentSum, total: totalSum); + }; + } + + public override void OnLoaded() + { + using var reader = new StreamReader(Assets.HfPackagesJson.Open()); + var packages = + JsonSerializer.Deserialize>( + reader.ReadToEnd(), + new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } } + ) ?? throw new InvalidOperationException("Failed to read hf-packages.json"); + + ItemsCache.EditDiff(packages, (a, b) => a.RepositoryPath == b.RepositoryPath); + } + + public void ClearSelection() + { + foreach (var category in Categories) + { + category.IsChecked = true; + category.IsChecked = false; + } + } + + public void SelectAll() + { + foreach (var category in Categories) + { + category.IsChecked = true; + } + } + + public async Task ImportSelected() + { + var selected = Categories.SelectMany(c => c.Items).Where(i => i.IsSelected).ToArray(); + + foreach (var viewModel in selected) + { + foreach (var file in viewModel.Item.Files) + { + var url = + $"https://huggingface.co/{viewModel.Item.RepositoryPath}/resolve/main/{file}?download=true"; + var sharedFolderType = viewModel.Item.ModelCategory.ConvertTo(); + var downloadPath = new FilePath( + Path.Combine( + settingsManager.ModelsDirectory, + sharedFolderType.GetDescription() ?? sharedFolderType.ToString(), + viewModel.Item.Subfolder ?? string.Empty, + file + ) + ); + Directory.CreateDirectory(downloadPath.Directory); + var download = trackedDownloadService.NewDownload(url, downloadPath); + download.ProgressUpdate += DownloadOnProgressUpdate; + download.Start(); + + await Task.Delay(Random.Shared.Next(50, 100)); + } + } + progressTimer.Start(); + } + + private void DownloadOnProgressUpdate(object? sender, ProgressReport e) + { + if (sender is not TrackedDownload trackedDownload) + return; + + progressReports[trackedDownload.Id] = e; + } + + partial void OnTotalProgressChanged(ProgressReport value) + { + if (Math.Abs(value.Percentage - 100) < 0.001f) + { + notificationService.Show( + "Download complete", + "All selected models have been downloaded.", + NotificationType.Success + ); + progressTimer.Stop(); + DelayedClearProgress(TimeSpan.FromSeconds(1.5)); + } + } + + private void DelayedClearProgress(TimeSpan delay) + { + Task.Delay(delay) + .ContinueWith(_ => + { + TotalProgress = new ProgressReport(0, 0); + }); + } +} diff --git a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml new file mode 100644 index 00000000..134b5da0 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Core/Helper/HardwareHelper.cs b/StabilityMatrix.Core/Helper/HardwareHelper.cs deleted file mode 100644 index b50fe42d..00000000 --- a/StabilityMatrix.Core/Helper/HardwareHelper.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System.Diagnostics; -using System.Runtime.Versioning; -using System.Text.RegularExpressions; -using Microsoft.Win32; - -namespace StabilityMatrix.Core.Helper; - -public static partial class HardwareHelper -{ - private static IReadOnlyList? cachedGpuInfos; - - private static string RunBashCommand(string command) - { - var processInfo = new ProcessStartInfo("bash", "-c \"" + command + "\"") - { - UseShellExecute = false, - RedirectStandardOutput = true - }; - - var process = Process.Start(processInfo); - - process.WaitForExit(); - - var output = process.StandardOutput.ReadToEnd(); - - return output; - } - - [SupportedOSPlatform("windows")] - private static IEnumerable IterGpuInfoWindows() - { - const string gpuRegistryKeyPath = - @"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}"; - - using var baseKey = Registry.LocalMachine.OpenSubKey(gpuRegistryKeyPath); - - if (baseKey == null) yield break; - - foreach (var subKeyName in baseKey.GetSubKeyNames().Where(k => k.StartsWith("0"))) - { - using var subKey = baseKey.OpenSubKey(subKeyName); - if (subKey != null) - { - yield return new GpuInfo - { - Name = subKey.GetValue("DriverDesc")?.ToString(), - MemoryBytes = Convert.ToUInt64(subKey.GetValue("HardwareInformation.qwMemorySize")), - }; - } - } - } - - [SupportedOSPlatform("linux")] - private static IEnumerable IterGpuInfoLinux() - { - var output = RunBashCommand("lspci | grep VGA"); - var gpuLines = output.Split("\n"); - - foreach (var line in gpuLines) - { - if (string.IsNullOrWhiteSpace(line)) continue; - - var gpuId = line.Split(' ')[0]; // The GPU ID is the first part of the line - var gpuOutput = RunBashCommand($"lspci -v -s {gpuId}"); - - ulong memoryBytes = 0; - string? name = null; - - // Parse output with regex - var match = Regex.Match(gpuOutput, @"VGA compatible controller: ([^\n]*)"); - if (match.Success) - { - name = match.Groups[1].Value.Trim(); - } - - match = Regex.Match(gpuOutput, @"prefetchable\) \[size=(\\d+)M\]"); - if (match.Success) - { - memoryBytes = ulong.Parse(match.Groups[1].Value) * 1024 * 1024; - } - - yield return new GpuInfo { Name = name, MemoryBytes = memoryBytes }; - } - } - - /// - /// Yields GpuInfo for each GPU in the system. - /// - public static IEnumerable IterGpuInfo() - { - if (Compat.IsWindows) - { - return IterGpuInfoWindows(); - } - else if (Compat.IsLinux) - { - // Since this requires shell commands, fetch cached value if available. - if (cachedGpuInfos is not null) - { - return cachedGpuInfos; - } - - // No cache, fetch and cache. - cachedGpuInfos = IterGpuInfoLinux().ToList(); - return cachedGpuInfos; - } - // TODO: Implement for macOS - return Enumerable.Empty(); - } - - /// - /// Return true if the system has at least one Nvidia GPU. - /// - public static bool HasNvidiaGpu() - { - return IterGpuInfo().Any(gpu => gpu.IsNvidia); - } - - /// - /// Return true if the system has at least one AMD GPU. - /// - public static bool HasAmdGpu() - { - return IterGpuInfo().Any(gpu => gpu.IsAmd); - } - - // Set ROCm for default if AMD and Linux - public static bool PreferRocm() => !HasNvidiaGpu() - && HasAmdGpu() - && Compat.IsLinux; - - // Set DirectML for default if AMD and Windows - public static bool PreferDirectML() => !HasNvidiaGpu() - && HasAmdGpu() - && Compat.IsWindows; -} - -public enum Level -{ - Unknown, - Low, - Medium, - High -} - -public record GpuInfo -{ - public string? Name { get; init; } = string.Empty; - public ulong MemoryBytes { get; init; } - public Level? MemoryLevel => MemoryBytes switch - { - <= 0 => Level.Unknown, - < 4 * Size.GiB => Level.Low, - < 8 * Size.GiB => Level.Medium, - _ => Level.High - }; - - public bool IsNvidia - { - get - { - var name = Name?.ToLowerInvariant(); - - if (string.IsNullOrEmpty(name)) return false; - - return name.Contains("nvidia") - || name.Contains("tesla"); - } - } - - public bool IsAmd => Name?.ToLowerInvariant().Contains("amd") ?? false; -} diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/CpuInfo.cs b/StabilityMatrix.Core/Helper/HardwareInfo/CpuInfo.cs new file mode 100644 index 00000000..7ea9b2b6 --- /dev/null +++ b/StabilityMatrix.Core/Helper/HardwareInfo/CpuInfo.cs @@ -0,0 +1,7 @@ +namespace StabilityMatrix.Core.Helper.HardwareInfo; + +public readonly record struct CpuInfo +{ + public string ProcessorCaption { get; init; } + public string ProcessorName { get; init; } +} diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/GpuInfo.cs b/StabilityMatrix.Core/Helper/HardwareInfo/GpuInfo.cs new file mode 100644 index 00000000..faabd558 --- /dev/null +++ b/StabilityMatrix.Core/Helper/HardwareInfo/GpuInfo.cs @@ -0,0 +1,31 @@ +namespace StabilityMatrix.Core.Helper.HardwareInfo; + +public record GpuInfo +{ + public int Index { get; init; } + public string? Name { get; init; } = string.Empty; + public ulong MemoryBytes { get; init; } + public MemoryLevel? MemoryLevel => + MemoryBytes switch + { + <= 0 => HardwareInfo.MemoryLevel.Unknown, + < 4 * Size.GiB => HardwareInfo.MemoryLevel.Low, + < 8 * Size.GiB => HardwareInfo.MemoryLevel.Medium, + _ => HardwareInfo.MemoryLevel.High + }; + + public bool IsNvidia + { + get + { + var name = Name?.ToLowerInvariant(); + + if (string.IsNullOrEmpty(name)) + return false; + + return name.Contains("nvidia") || name.Contains("tesla"); + } + } + + public bool IsAmd => Name?.Contains("amd", StringComparison.OrdinalIgnoreCase) ?? false; +} diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/HardwareHelper.cs b/StabilityMatrix.Core/Helper/HardwareInfo/HardwareHelper.cs new file mode 100644 index 00000000..b86216a5 --- /dev/null +++ b/StabilityMatrix.Core/Helper/HardwareInfo/HardwareHelper.cs @@ -0,0 +1,241 @@ +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Text.RegularExpressions; +using Hardware.Info; +using Microsoft.Win32; + +namespace StabilityMatrix.Core.Helper.HardwareInfo; + +public static partial class HardwareHelper +{ + private static IReadOnlyList? cachedGpuInfos; + + private static readonly Lazy HardwareInfoLazy = + new(() => new Hardware.Info.HardwareInfo()); + + public static IHardwareInfo HardwareInfo => HardwareInfoLazy.Value; + + private static string RunBashCommand(string command) + { + var processInfo = new ProcessStartInfo("bash", "-c \"" + command + "\"") + { + UseShellExecute = false, + RedirectStandardOutput = true + }; + + var process = Process.Start(processInfo); + + process.WaitForExit(); + + var output = process.StandardOutput.ReadToEnd(); + + return output; + } + + [SupportedOSPlatform("windows")] + private static IEnumerable IterGpuInfoWindows() + { + const string gpuRegistryKeyPath = + @"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}"; + + using var baseKey = Registry.LocalMachine.OpenSubKey(gpuRegistryKeyPath); + + if (baseKey == null) + yield break; + + var gpuIndex = 0; + + foreach (var subKeyName in baseKey.GetSubKeyNames().Where(k => k.StartsWith("0"))) + { + using var subKey = baseKey.OpenSubKey(subKeyName); + if (subKey != null) + { + yield return new GpuInfo + { + Index = gpuIndex++, + Name = subKey.GetValue("DriverDesc")?.ToString(), + MemoryBytes = Convert.ToUInt64( + subKey.GetValue("HardwareInformation.qwMemorySize") + ), + }; + } + } + } + + [SupportedOSPlatform("linux")] + private static IEnumerable IterGpuInfoLinux() + { + var output = RunBashCommand("lspci | grep VGA"); + var gpuLines = output.Split("\n"); + + var gpuIndex = 0; + + foreach (var line in gpuLines) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + var gpuId = line.Split(' ')[0]; // The GPU ID is the first part of the line + var gpuOutput = RunBashCommand($"lspci -v -s {gpuId}"); + + ulong memoryBytes = 0; + string? name = null; + + // Parse output with regex + var match = Regex.Match(gpuOutput, @"VGA compatible controller: ([^\n]*)"); + if (match.Success) + { + name = match.Groups[1].Value.Trim(); + } + + match = Regex.Match(gpuOutput, @"prefetchable\) \[size=(\\d+)M\]"); + if (match.Success) + { + memoryBytes = ulong.Parse(match.Groups[1].Value) * 1024 * 1024; + } + + yield return new GpuInfo + { + Index = gpuIndex++, + Name = name, + MemoryBytes = memoryBytes + }; + } + } + + /// + /// Yields GpuInfo for each GPU in the system. + /// + public static IEnumerable IterGpuInfo() + { + if (Compat.IsWindows) + { + return IterGpuInfoWindows(); + } + else if (Compat.IsLinux) + { + // Since this requires shell commands, fetch cached value if available. + if (cachedGpuInfos is not null) + { + return cachedGpuInfos; + } + + // No cache, fetch and cache. + cachedGpuInfos = IterGpuInfoLinux().ToList(); + return cachedGpuInfos; + } + // TODO: Implement for macOS + return Enumerable.Empty(); + } + + /// + /// Return true if the system has at least one Nvidia GPU. + /// + public static bool HasNvidiaGpu() + { + return IterGpuInfo().Any(gpu => gpu.IsNvidia); + } + + /// + /// Return true if the system has at least one AMD GPU. + /// + public static bool HasAmdGpu() + { + return IterGpuInfo().Any(gpu => gpu.IsAmd); + } + + // Set ROCm for default if AMD and Linux + public static bool PreferRocm() => !HasNvidiaGpu() && HasAmdGpu() && Compat.IsLinux; + + // Set DirectML for default if AMD and Windows + public static bool PreferDirectML() => !HasNvidiaGpu() && HasAmdGpu() && Compat.IsWindows; + + /// + /// Gets the total and available physical memory in bytes. + /// + public static MemoryInfo GetMemoryInfo() => + Compat.IsWindows ? GetMemoryInfoImplWindows() : GetMemoryInfoImplGeneric(); + + [SupportedOSPlatform("windows")] + private static MemoryInfo GetMemoryInfoImplWindows() + { + var memoryStatus = new Win32MemoryStatusEx(); + + if (!GlobalMemoryStatusEx(ref memoryStatus)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + if (!GetPhysicallyInstalledSystemMemory(out var installedMemoryKb)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + return new MemoryInfo + { + TotalInstalledBytes = (ulong)installedMemoryKb * 1024, + TotalPhysicalBytes = memoryStatus.UllTotalPhys, + AvailablePhysicalBytes = memoryStatus.UllAvailPhys + }; + } + + private static MemoryInfo GetMemoryInfoImplGeneric() + { + HardwareInfo.RefreshMemoryList(); + + return new MemoryInfo + { + TotalPhysicalBytes = HardwareInfo.MemoryStatus.TotalPhysical, + AvailablePhysicalBytes = HardwareInfo.MemoryStatus.AvailablePhysical + }; + } + + /// + /// Gets cpu info + /// + public static Task GetCpuInfoAsync() => + Compat.IsWindows ? Task.FromResult(GetCpuInfoImplWindows()) : GetCpuInfoImplGenericAsync(); + + [SupportedOSPlatform("windows")] + private static CpuInfo GetCpuInfoImplWindows() + { + var info = new CpuInfo(); + + using var processorKey = Registry.LocalMachine.OpenSubKey( + @"Hardware\Description\System\CentralProcessor\0", + RegistryKeyPermissionCheck.ReadSubTree + ); + + if (processorKey?.GetValue("ProcessorNameString") is string processorName) + { + info = info with { ProcessorCaption = processorName.Trim() }; + } + + return info; + } + + private static Task GetCpuInfoImplGenericAsync() + { + return Task.Run(() => + { + HardwareInfo.RefreshCPUList(); + + return new CpuInfo + { + ProcessorCaption = HardwareInfo.CpuList.FirstOrDefault()?.Caption.Trim() ?? "" + }; + }); + } + + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool GetPhysicallyInstalledSystemMemory(out long totalMemoryInKilobytes); + + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool GlobalMemoryStatusEx(ref Win32MemoryStatusEx lpBuffer); +} diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/MemoryInfo.cs b/StabilityMatrix.Core/Helper/HardwareInfo/MemoryInfo.cs new file mode 100644 index 00000000..821f35fd --- /dev/null +++ b/StabilityMatrix.Core/Helper/HardwareInfo/MemoryInfo.cs @@ -0,0 +1,10 @@ +namespace StabilityMatrix.Core.Helper.HardwareInfo; + +public readonly record struct MemoryInfo +{ + public ulong TotalInstalledBytes { get; init; } + + public ulong TotalPhysicalBytes { get; init; } + + public ulong AvailablePhysicalBytes { get; init; } +} diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/MemoryLevel.cs b/StabilityMatrix.Core/Helper/HardwareInfo/MemoryLevel.cs new file mode 100644 index 00000000..4f66acbf --- /dev/null +++ b/StabilityMatrix.Core/Helper/HardwareInfo/MemoryLevel.cs @@ -0,0 +1,9 @@ +namespace StabilityMatrix.Core.Helper.HardwareInfo; + +public enum MemoryLevel +{ + Unknown, + Low, + Medium, + High +} diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs b/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs new file mode 100644 index 00000000..43e399b9 --- /dev/null +++ b/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs @@ -0,0 +1,17 @@ +using System.Runtime.InteropServices; + +namespace StabilityMatrix.Core.Helper.HardwareInfo; + +[StructLayout(LayoutKind.Sequential)] +public struct Win32MemoryStatusEx +{ + public uint DwLength = (uint)Marshal.SizeOf(typeof(Win32MemoryStatusEx)); + public uint DwMemoryLoad = 0; + public ulong UllTotalPhys = 0; + public ulong UllAvailPhys = 0; + public ulong UllTotalPageFile = 0; + public ulong UllAvailPageFile = 0; + public ulong UllTotalVirtual = 0; + public ulong UllAvailVirtual = 0; + public ulong UllAvailExtendedVirtual = 0; +} diff --git a/StabilityMatrix.Core/Helper/Size.cs b/StabilityMatrix.Core/Helper/Size.cs index 4f31ba60..e1249b7f 100644 --- a/StabilityMatrix.Core/Helper/Size.cs +++ b/StabilityMatrix.Core/Helper/Size.cs @@ -9,25 +9,60 @@ public static class Size public const ulong MiB = KiB * 1024; public const ulong GiB = MiB * 1024; - public static string FormatBytes(ulong bytes) + private static string TrimZero(string value) + { + return value.TrimEnd('0').TrimEnd('.'); + } + + public static string FormatBytes(ulong bytes, bool trimZero = false) { return bytes switch { - < KiB => $"{bytes} B", - < MiB => $"{bytes / (double)KiB:0.0} KiB", - < GiB => $"{bytes / (double)MiB:0.0} MiB", - _ => $"{bytes / (double)GiB:0.0} GiB" + < KiB => $"{bytes:0} Bytes", + < MiB + => ( + trimZero + ? $"{bytes / (double)KiB:0.0}".TrimEnd('0').TrimEnd('.') + : $"{bytes / (double)KiB:0.0}" + ) + " KiB", + < GiB + => ( + trimZero + ? $"{bytes / (double)MiB:0.0}".TrimEnd('0').TrimEnd('.') + : $"{bytes / (double)MiB:0.0}" + ) + " MiB", + _ + => ( + trimZero + ? $"{bytes / (double)GiB:0.0}".TrimEnd('0').TrimEnd('.') + : $"{bytes / (double)GiB:0.0}" + ) + " GiB" }; } - public static string FormatBase10Bytes(ulong bytes) + public static string FormatBase10Bytes(ulong bytes, bool trimZero = false) { return bytes switch { - < KiB => $"{bytes} Bytes", - < MiB => $"{bytes / (double)KiB:0.0} KB", - < GiB => $"{bytes / (double)MiB:0.0} MB", - _ => $"{bytes / (double)GiB:0.00} GB" + < KiB => $"{bytes:0} Bytes", + < MiB + => ( + trimZero + ? $"{bytes / (double)KiB:0.0}".TrimEnd('0').TrimEnd('.') + : $"{bytes / (double)KiB:0.0}" + ) + " KB", + < GiB + => ( + trimZero + ? $"{bytes / (double)MiB:0.0}".TrimEnd('0').TrimEnd('.') + : $"{bytes / (double)MiB:0.0}" + ) + " MB", + _ + => ( + trimZero + ? $"{bytes / (double)GiB:0.00}".TrimEnd('0').TrimEnd('.') + : $"{bytes / (double)GiB:0.00}" + ) + " GB" }; } diff --git a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs index bad8db64..3d6ce702 100644 --- a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs +++ b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs @@ -5,6 +5,7 @@ using NLog; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; @@ -103,8 +104,8 @@ public class A3WebUI : BaseGitPackage .Select(gpu => gpu.MemoryLevel) .Max() switch { - Level.Low => "--lowvram", - Level.Medium => "--medvram", + MemoryLevel.Low => "--lowvram", + MemoryLevel.Medium => "--medvram", _ => null }, Options = new() { "--lowvram", "--medvram", "--medvram-sdxl" } diff --git a/StabilityMatrix.Core/Models/Packages/BasePackage.cs b/StabilityMatrix.Core/Models/Packages/BasePackage.cs index 29ff06b9..ec20e75f 100644 --- a/StabilityMatrix.Core/Models/Packages/BasePackage.cs +++ b/StabilityMatrix.Core/Models/Packages/BasePackage.cs @@ -1,5 +1,6 @@ using Octokit; using StabilityMatrix.Core.Helper; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; diff --git a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs index c7a824b7..10999218 100644 --- a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs +++ b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs @@ -4,6 +4,7 @@ using NLog; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; @@ -92,8 +93,8 @@ public class ComfyUI : BaseGitPackage .Select(gpu => gpu.MemoryLevel) .Max() switch { - Level.Low => "--lowvram", - Level.Medium => "--normalvram", + MemoryLevel.Low => "--lowvram", + MemoryLevel.Medium => "--normalvram", _ => null }, Options = { "--highvram", "--normalvram", "--lowvram", "--novram" } diff --git a/StabilityMatrix.Core/Models/Packages/Fooocus.cs b/StabilityMatrix.Core/Models/Packages/Fooocus.cs index b25e2ed1..d55f4d69 100644 --- a/StabilityMatrix.Core/Models/Packages/Fooocus.cs +++ b/StabilityMatrix.Core/Models/Packages/Fooocus.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; @@ -84,8 +85,8 @@ public class Fooocus : BaseGitPackage .Select(gpu => gpu.MemoryLevel) .Max() switch { - Level.Low => "--lowvram", - Level.Medium => "--normalvram", + MemoryLevel.Low => "--lowvram", + MemoryLevel.Medium => "--normalvram", _ => null }, Options = { "--highvram", "--normalvram", "--lowvram", "--novram" } diff --git a/StabilityMatrix.Core/Models/Packages/KohyaSs.cs b/StabilityMatrix.Core/Models/Packages/KohyaSs.cs index 99b2a6a3..ca4535fd 100644 --- a/StabilityMatrix.Core/Models/Packages/KohyaSs.cs +++ b/StabilityMatrix.Core/Models/Packages/KohyaSs.cs @@ -4,6 +4,7 @@ using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Extensions; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; diff --git a/StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs b/StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs index 9b4a9b45..7ff9f792 100644 --- a/StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs +++ b/StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs @@ -5,6 +5,7 @@ using NLog; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; @@ -104,8 +105,8 @@ public class StableDiffusionUx : BaseGitPackage .Select(gpu => gpu.MemoryLevel) .Max() switch { - Level.Low => "--lowvram", - Level.Medium => "--medvram", + MemoryLevel.Low => "--lowvram", + MemoryLevel.Medium => "--medvram", _ => null }, Options = new() { "--lowvram", "--medvram", "--medvram-sdxl" } diff --git a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs index 6a5c6308..7a300516 100644 --- a/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs +++ b/StabilityMatrix.Core/Models/Packages/VladAutomatic.cs @@ -7,6 +7,7 @@ using NLog; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Processes; @@ -110,8 +111,8 @@ public class VladAutomatic : BaseGitPackage .Select(gpu => gpu.MemoryLevel) .Max() switch { - Level.Low => "--lowvram", - Level.Medium => "--medvram", + MemoryLevel.Low => "--lowvram", + MemoryLevel.Medium => "--medvram", _ => null }, Options = new() { "--lowvram", "--medvram" } diff --git a/StabilityMatrix.Core/StabilityMatrix.Core.csproj b/StabilityMatrix.Core/StabilityMatrix.Core.csproj index 254f8efe..504924d6 100644 --- a/StabilityMatrix.Core/StabilityMatrix.Core.csproj +++ b/StabilityMatrix.Core/StabilityMatrix.Core.csproj @@ -7,6 +7,7 @@ enable true true + true @@ -24,6 +25,7 @@ + From 006b0d60da4ae14c9dcd6d88ce47ce1d72b097b8 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 04:03:50 -0500 Subject: [PATCH 028/166] Add default constructor --- StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs b/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs index 43e399b9..13811565 100644 --- a/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs +++ b/StabilityMatrix.Core/Helper/HardwareInfo/Win32MemoryStatusEx.cs @@ -14,4 +14,6 @@ public struct Win32MemoryStatusEx public ulong UllTotalVirtual = 0; public ulong UllAvailVirtual = 0; public ulong UllAvailExtendedVirtual = 0; + + public Win32MemoryStatusEx() { } } From 428c48ab4a712c802af95af14d4fb63db93ed211 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 04:08:22 -0500 Subject: [PATCH 029/166] Cleanup and change system information title --- .../Languages/Resources.Designer.cs | 18 +++++++++--------- .../Languages/Resources.resx | 4 ++-- .../Views/Settings/MainSettingsPage.axaml | 12 +----------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index b5468fdf..6cc3bce5 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -1193,15 +1193,6 @@ namespace StabilityMatrix.Avalonia.Languages { } } - /// - /// Looks up a localized string similar to Information. - /// - public static string Label_Information { - get { - return ResourceManager.GetString("Label_Information", resourceCulture); - } - } - /// /// Looks up a localized string similar to Inner exception. /// @@ -1877,6 +1868,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to System Information. + /// + public static string Label_SystemInformation { + get { + return ResourceManager.GetString("Label_SystemInformation", resourceCulture); + } + } + /// /// Looks up a localized string similar to Text to Image. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index 2c3ff57f..529fa151 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -843,7 +843,7 @@ Tags file to use for suggesting completions (Supports the a1111-sd-webui-tagcomplete .csv format) - - Information + + System Information diff --git a/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml b/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml index e35b1bbc..21332c4a 100644 --- a/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/Settings/MainSettingsPage.axaml @@ -301,7 +301,7 @@ - + - - From 19457c6a82f9586182035637c77db75a4df87598 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 04:09:29 -0500 Subject: [PATCH 030/166] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b57bb65..9cb0ee26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ 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.7.0-pre.2 +### Added +- Added System Information section to Settings +### Changed +- Moved Inference Settings to subpage ### Fixed - Fixed crash when loading an empty settings file - Improve Settings save and load performance with .NET 8 Source Generating Serialization From 75809f231b68f885e08885d357bb705cb90cddaa Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 04:09:36 -0500 Subject: [PATCH 031/166] Update dotnet-tools.json --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 5b4bb470..f59ebfb8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,10 +15,10 @@ ] }, "csharpier": { - "version": "0.25.0", + "version": "0.26.3", "commands": [ "dotnet-csharpier" ] } } -} +} \ No newline at end of file From c3ee009662bb6820686dbf3d4bdbcf20920deb1c Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:12:34 -0500 Subject: [PATCH 032/166] Use manual quoting for 7z args --- StabilityMatrix.Core/Helper/ArchiveHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/StabilityMatrix.Core/Helper/ArchiveHelper.cs b/StabilityMatrix.Core/Helper/ArchiveHelper.cs index 40c5d53b..c2621af7 100644 --- a/StabilityMatrix.Core/Helper/ArchiveHelper.cs +++ b/StabilityMatrix.Core/Helper/ArchiveHelper.cs @@ -86,11 +86,11 @@ public static partial class ArchiveHelper public static async Task Extract7Z(string archivePath, string extractDirectory) { + var args = + $"x {ProcessRunner.Quote(archivePath)} -o{ProcessRunner.Quote(extractDirectory)} -y"; + var result = await ProcessRunner - .GetProcessResultAsync( - SevenZipPath, - new[] { "x", archivePath, "-o" + ProcessRunner.Quote(extractDirectory), "-y" } - ) + .GetProcessResultAsync(SevenZipPath, args) .ConfigureAwait(false); result.EnsureSuccessExitCode(); From 42bb74389bd046f5750d8ba82f5ddf94778074a9 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:13:41 -0500 Subject: [PATCH 033/166] Cleanup usings --- StabilityMatrix.Core/Helper/ArchiveHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/StabilityMatrix.Core/Helper/ArchiveHelper.cs b/StabilityMatrix.Core/Helper/ArchiveHelper.cs index c2621af7..e0326ba3 100644 --- a/StabilityMatrix.Core/Helper/ArchiveHelper.cs +++ b/StabilityMatrix.Core/Helper/ArchiveHelper.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; using NLog; From 432434919093268aefbd57e7174acb947afa4cab Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:14:02 -0500 Subject: [PATCH 034/166] Add ConfigureAwaits --- StabilityMatrix.Core/Helper/ArchiveHelper.cs | 62 ++++++++++---------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/StabilityMatrix.Core/Helper/ArchiveHelper.cs b/StabilityMatrix.Core/Helper/ArchiveHelper.cs index e0326ba3..04042276 100644 --- a/StabilityMatrix.Core/Helper/ArchiveHelper.cs +++ b/StabilityMatrix.Core/Helper/ArchiveHelper.cs @@ -58,8 +58,8 @@ public static partial class ArchiveHelper public static async Task TestArchive(string archivePath) { var process = ProcessRunner.StartAnsiProcess(SevenZipPath, new[] { "t", archivePath }); - await process.WaitForExitAsync(); - var output = await process.StandardOutput.ReadToEndAsync(); + await process.WaitForExitAsync().ConfigureAwait(false); + var output = await process.StandardOutput.ReadToEndAsync().ConfigureAwait(false); var matches = Regex7ZOutput().Matches(output); var size = ulong.Parse(matches[0].Value); var compressed = ulong.Parse(matches[1].Value); @@ -184,7 +184,7 @@ public static partial class ArchiveHelper throw new ArgumentException("Archive must be a zipped tar."); } // Extract the tar.gz to tar - await Extract7Z(archivePath, extractDirectory); + await Extract7Z(archivePath, extractDirectory).ConfigureAwait(false); // Extract the tar var tarPath = Path.Combine(extractDirectory, Path.GetFileNameWithoutExtension(archivePath)); @@ -195,7 +195,7 @@ public static partial class ArchiveHelper try { - return await Extract7Z(tarPath, extractDirectory); + return await Extract7Z(tarPath, extractDirectory).ConfigureAwait(false); } finally { @@ -214,11 +214,11 @@ public static partial class ArchiveHelper { if (archivePath.EndsWith(".tar.gz")) { - return await Extract7ZTar(archivePath, extractDirectory); + return await Extract7ZTar(archivePath, extractDirectory).ConfigureAwait(false); } else { - return await Extract7Z(archivePath, extractDirectory); + return await Extract7Z(archivePath, extractDirectory).ConfigureAwait(false); } } @@ -240,7 +240,7 @@ public static partial class ArchiveHelper var count = 0ul; // Get true size - var (total, _) = await TestArchive(archivePath); + var (total, _) = await TestArchive(archivePath).ConfigureAwait(false); // If not available, use the size of the archive file if (total == 0) @@ -265,32 +265,34 @@ public static partial class ArchiveHelper }; } - await Task.Factory.StartNew( - () => - { - var extractOptions = new ExtractionOptions + await Task.Factory + .StartNew( + () => { - Overwrite = true, - ExtractFullPath = true, - }; - using var stream = File.OpenRead(archivePath); - using var archive = ReaderFactory.Open(stream); + var extractOptions = new ExtractionOptions + { + Overwrite = true, + ExtractFullPath = true, + }; + using var stream = File.OpenRead(archivePath); + using var archive = ReaderFactory.Open(stream); - // Start the progress reporting timer - progressMonitor?.Start(); + // Start the progress reporting timer + progressMonitor?.Start(); - while (archive.MoveToNextEntry()) - { - var entry = archive.Entry; - if (!entry.IsDirectory) + while (archive.MoveToNextEntry()) { - count += (ulong)entry.CompressedSize; + var entry = archive.Entry; + if (!entry.IsDirectory) + { + count += (ulong)entry.CompressedSize; + } + archive.WriteEntryToDirectory(outputDirectory, extractOptions); } - archive.WriteEntryToDirectory(outputDirectory, extractOptions); - } - }, - TaskCreationOptions.LongRunning - ); + }, + TaskCreationOptions.LongRunning + ) + .ConfigureAwait(false); progress?.Report(new ProgressReport(progress: 1, message: "Done extracting")); progressMonitor?.Stop(); @@ -304,7 +306,7 @@ public static partial class ArchiveHelper public static async Task ExtractManaged(string archivePath, string outputDirectory) { await using var stream = File.OpenRead(archivePath); - await ExtractManaged(stream, outputDirectory); + await ExtractManaged(stream, outputDirectory).ConfigureAwait(false); } /// @@ -380,7 +382,7 @@ public static partial class ArchiveHelper // Write file await using var entryStream = reader.OpenEntryStream(); await using var fileStream = File.Create(outputPath); - await entryStream.CopyToAsync(fileStream); + await entryStream.CopyToAsync(fileStream).ConfigureAwait(false); } } } From 107a56287ef9c17adefbae4b27bc7240b301fc9b Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:18:25 -0500 Subject: [PATCH 035/166] Add changelog for fix --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41817ff0..efdb5b15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to Stability Matrix will be documented in this file. 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.6.7 +### Fixed +- Fixed prerequisite install not unpacking due to improperly formatted 7z argument (Caused the "python310._pth FileNotFoundException") + ## v2.6.6 ### Fixed - Fixed [#297](https://github.com/LykosAI/StabilityMatrix/issues/297) - Model browser LiteAsyncException occuring when fetching entries with unrecognized values from enum name changes From 208ee648ebd0633c4a7591549c6daff89eada5c2 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:23:10 -0500 Subject: [PATCH 036/166] Fix backwards git init args --- StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs b/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs index e2686bb2..cb2a742c 100644 --- a/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs +++ b/StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs @@ -309,7 +309,7 @@ public abstract class BaseGitPackage : BasePackage new ProgressReport(-1f, "Initializing git repo", isIndeterminate: true) ); await PrerequisiteHelper - .RunGit(installedPackage.FullPath!, onConsoleOutput, "init") + .RunGit("init", onConsoleOutput, installedPackage.FullPath) .ConfigureAwait(false); await PrerequisiteHelper .RunGit( From 71c844d0edfb48be282d9fbc7d464bab78154a9f Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:25:40 -0500 Subject: [PATCH 037/166] Add fix changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index efdb5b15..26905e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ## v2.6.7 ### Fixed - Fixed prerequisite install not unpacking due to improperly formatted 7z argument (Caused the "python310._pth FileNotFoundException") +- Fixed [#301](https://github.com/LykosAI/StabilityMatrix/issues/301) - Package updates failing silently because of a PortableGit error ## v2.6.6 ### Fixed From 7e0b83d987c1186d1c36aaee1a0d0dcd76d5f1eb Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 1 Dec 2023 16:39:06 -0500 Subject: [PATCH 038/166] Add fallback handling for changelog fetch errors --- .../ViewModels/Dialogs/UpdateViewModel.cs | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs index 1078eb02..357b28ad 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs @@ -171,25 +171,34 @@ public partial class UpdateViewModel : ContentDialogViewModelBase internal async Task GetReleaseNotes(string changelogUrl) { using var client = httpClientFactory.CreateClient(); - var response = await client.GetAsync(changelogUrl); - if (response.IsSuccessStatusCode) - { - var changelog = await response.Content.ReadAsStringAsync(); - // Formatting for new changelog format - // https://keepachangelog.com/en/1.1.0/ - if (changelogUrl.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + try + { + var response = await client.GetAsync(changelogUrl); + if (response.IsSuccessStatusCode) { - return FormatChangelog(changelog, Compat.AppVersion) - ?? "## Unable to format release notes"; + var changelog = await response.Content.ReadAsStringAsync(); + + // Formatting for new changelog format + // https://keepachangelog.com/en/1.1.0/ + if (changelogUrl.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + { + return FormatChangelog(changelog, Compat.AppVersion) + ?? "## Unable to format release notes"; + } + + return changelog; } - return changelog; + return "## Unable to load release notes"; } - else + catch (HttpRequestException e) { - return "## Unable to load release notes"; + return $"## Unable to fetch release notes ({e.StatusCode})\n\n[{changelogUrl}]({changelogUrl})"; } + catch (TaskCanceledException) { } + + return $"## Unable to fetch release notes\n\n[{changelogUrl}]({changelogUrl})"; } partial void OnUpdateInfoChanged(UpdateInfo? value) From 4815c812b9faef70454f425f6b7908098632052d Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 2 Dec 2023 10:05:28 -0800 Subject: [PATCH 039/166] added MetadataImportService for handling model metadata imports & added the ability to "find connected metadata" for any file/folder/etc --- .../DesignData/DesignData.cs | 8 +- .../DesignData/MockMetadataImportService.cs | 36 ++ .../Languages/Resources.Designer.cs | 11 +- .../Languages/Resources.resx | 5 +- .../Styles/SplitButtonStyles.axaml | 63 ++++ .../CheckpointManager/CheckpointFile.cs | 51 ++- .../CheckpointManager/CheckpointFolder.cs | 61 ++-- .../ViewModels/CheckpointsPageViewModel.cs | 58 +++- .../Views/CheckpointBrowserPage.axaml | 2 +- .../Views/CheckpointsPage.axaml | 91 +++++- .../InferenceConnectionHelpDialog.axaml | 2 +- .../Views/HuggingFacePage.axaml | 151 +++++---- StabilityMatrix.Core/Helper/ModelFinder.cs | 2 +- .../Models/ConnectedModelInfo.cs | 4 +- .../Models/Database/LocalModelFile.cs | 6 +- .../Services/IMetadataImportService.cs | 24 ++ .../Services/MetadataImportService.cs | 308 ++++++++++++++++++ 17 files changed, 769 insertions(+), 114 deletions(-) create mode 100644 StabilityMatrix.Avalonia/DesignData/MockMetadataImportService.cs create mode 100644 StabilityMatrix.Core/Services/IMetadataImportService.cs create mode 100644 StabilityMatrix.Core/Services/MetadataImportService.cs diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 53d01d6e..d4a690ba 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -124,7 +124,8 @@ public static class DesignData .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); // Placeholder services that nobody should need during design time services @@ -147,6 +148,7 @@ public static class DesignData var modelFinder = Services.GetRequiredService(); var packageFactory = Services.GetRequiredService(); var notificationService = Services.GetRequiredService(); + var modelImportService = Services.GetRequiredService(); LaunchOptionsViewModel = Services.GetRequiredService(); LaunchOptionsViewModel.Cards = new[] @@ -183,7 +185,7 @@ public static class DesignData CheckpointsPageViewModel.CheckpointFoldersCache, new CheckpointFolder[] { - new(settingsManager, downloadService, modelFinder, notificationService) + new(settingsManager, downloadService, modelFinder, notificationService, modelImportService) { DirectoryPath = "Models/StableDiffusion", DisplayedCheckpointFiles = new ObservableCollectionExtended() @@ -217,7 +219,7 @@ public static class DesignData }, }, }, - new(settingsManager, downloadService, modelFinder, notificationService) + new(settingsManager, downloadService, modelFinder, notificationService, modelImportService) { Title = "Lora", DirectoryPath = "Packages/Lora", diff --git a/StabilityMatrix.Avalonia/DesignData/MockMetadataImportService.cs b/StabilityMatrix.Avalonia/DesignData/MockMetadataImportService.cs new file mode 100644 index 00000000..3021ddc0 --- /dev/null +++ b/StabilityMatrix.Avalonia/DesignData/MockMetadataImportService.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.FileInterfaces; +using StabilityMatrix.Core.Models.Progress; +using StabilityMatrix.Core.Services; + +namespace StabilityMatrix.Avalonia.DesignData; + +public class MockMetadataImportService : IMetadataImportService +{ + public Task ScanDirectoryForMissingInfo( + DirectoryPath directory, + IProgress? progress = null + ) + { + return Task.CompletedTask; + } + + public Task GetMetadataForFile( + FilePath filePath, + IProgress? progress = null, + bool forceReimport = false + ) + { + return null; + } + + public Task UpdateExistingMetadata( + DirectoryPath directory, + IProgress? progress = null + ) + { + return Task.CompletedTask; + } +} diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index f41d4407..22d30cc1 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -572,6 +572,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Update Existing Metadata. + /// + public static string Action_UpdateExistingMetadata { + get { + return ResourceManager.GetString("Action_UpdateExistingMetadata", resourceCulture); + } + } + /// /// Looks up a localized string similar to Upgrade. /// @@ -1014,7 +1023,7 @@ namespace StabilityMatrix.Avalonia.Languages { } /// - /// Looks up a localized string similar to Emebeddings / Textual Inversion. + /// Looks up a localized string similar to Embeddings / Textual Inversion. /// public static string Label_EmbeddingsOrTextualInversion { get { diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index fa76512d..14e6479d 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -175,7 +175,7 @@ Deemphasis - Emebeddings / Textual Inversion + Embeddings / Textual Inversion Networks (Lora / LyCORIS) @@ -816,4 +816,7 @@ Open on Hugging Face + + Update Existing Metadata + diff --git a/StabilityMatrix.Avalonia/Styles/SplitButtonStyles.axaml b/StabilityMatrix.Avalonia/Styles/SplitButtonStyles.axaml index 94480cff..6a2bd534 100644 --- a/StabilityMatrix.Avalonia/Styles/SplitButtonStyles.axaml +++ b/StabilityMatrix.Avalonia/Styles/SplitButtonStyles.axaml @@ -11,6 +11,7 @@ + @@ -417,6 +418,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/StabilityMatrix.Avalonia/Views/CheckpointBrowserPage.axaml.cs b/StabilityMatrix.Avalonia/Views/CheckpointBrowserPage.axaml.cs index df465d31..9b2f245b 100644 --- a/StabilityMatrix.Avalonia/Views/CheckpointBrowserPage.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/CheckpointBrowserPage.axaml.cs @@ -1,9 +1,4 @@ -using System.Diagnostics; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Markup.Xaml; -using StabilityMatrix.Avalonia.Controls; -using StabilityMatrix.Avalonia.ViewModels; +using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Core.Attributes; namespace StabilityMatrix.Avalonia.Views; @@ -15,26 +10,4 @@ public partial class CheckpointBrowserPage : UserControlBase { InitializeComponent(); } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - - private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e) - { - if (sender is not ScrollViewer scrollViewer) - return; - - var isAtEnd = scrollViewer.Offset == scrollViewer.ScrollBarMaximum; - Debug.WriteLine($"IsAtEnd: {isAtEnd}"); - } - - private void InputElement_OnKeyDown(object? sender, KeyEventArgs e) - { - if (e.Key == Key.Escape && DataContext is CheckpointBrowserViewModel viewModel) - { - viewModel.ClearSearchQuery(); - } - } } diff --git a/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml new file mode 100644 index 00000000..36eccf9f --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml @@ -0,0 +1,536 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml.cs b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml.cs new file mode 100644 index 00000000..e149bf5f --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.ViewModels; +using StabilityMatrix.Core.Attributes; +using CivitAiBrowserViewModel = StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser.CivitAiBrowserViewModel; + +namespace StabilityMatrix.Avalonia.Views; + +[Singleton] +public partial class CivitAiBrowserPage : UserControlBase +{ + public CivitAiBrowserPage() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e) + { + if (sender is not ScrollViewer scrollViewer) + return; + + var isAtEnd = scrollViewer.Offset == scrollViewer.ScrollBarMaximum; + Debug.WriteLine($"IsAtEnd: {isAtEnd}"); + } + + private void InputElement_OnKeyDown(object? sender, KeyEventArgs e) + { + if (e.Key == Key.Escape && DataContext is CivitAiBrowserViewModel viewModel) + { + viewModel.ClearSearchQuery(); + } + } +} diff --git a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml index 41cb330e..3ee7a404 100644 --- a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml +++ b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml @@ -8,45 +8,35 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData" xmlns:ui="using:FluentAvalonia.UI.Controls" - xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels" xmlns:huggingFacePage="clr-namespace:StabilityMatrix.Avalonia.ViewModels.HuggingFacePage" - xmlns:huggingFace="clr-namespace:StabilityMatrix.Avalonia.Models.HuggingFace" xmlns:helpers="clr-namespace:StabilityMatrix.Avalonia.Helpers" + xmlns:checkpointBrowser="clr-namespace:StabilityMatrix.Avalonia.ViewModels.CheckpointBrowser" d:DataContext="{x:Static mocks:DesignData.HuggingFacePageViewModel}" d:DesignHeight="650" d:DesignWidth="800" x:CompileBindings="True" - x:DataType="vm:HuggingFacePageViewModel" + x:DataType="checkpointBrowser:HuggingFacePageViewModel" Focusable="True" mc:Ignorable="d"> - - - - - - - - + + + + @@ -66,6 +56,7 @@ Command="{Binding ClearSelection}" IsEnabled="{Binding !!NumSelected}" Label="{x:Static lang:Resources.Action_ClearSelection}" /> + From 8282c077fdae6252847279476975c86436cd9afe Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 2 Dec 2023 17:48:57 -0800 Subject: [PATCH 059/166] remove old/broken/test stuff --- StabilityMatrix.Avalonia/App.axaml.cs | 217 +++++++----------- .../ViewModels/Base/TabViewModelBase.cs | 11 +- .../HuggingFacePageViewModel.cs | 30 +-- 3 files changed, 92 insertions(+), 166 deletions(-) diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index fc44f630..c1484492 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -1,7 +1,3 @@ -#if DEBUG -using StabilityMatrix.Avalonia.Diagnostics.LogViewer; -using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions; -#endif using System; using System.Collections.Generic; using System.Diagnostics; @@ -64,6 +60,11 @@ using StabilityMatrix.Core.Services; using Application = Avalonia.Application; using DrawingColor = System.Drawing.Color; using LogLevel = Microsoft.Extensions.Logging.LogLevel; +#if DEBUG +using StabilityMatrix.Avalonia.Diagnostics.LogViewer; +using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions; +#endif + namespace StabilityMatrix.Avalonia; @@ -77,8 +78,7 @@ public sealed class App : Application public static TopLevel TopLevel => TopLevel.GetTopLevel(VisualRoot)!; - internal static bool IsHeadlessMode => - TopLevel.TryGetPlatformHandle()?.HandleDescriptor is null or "STUB"; + internal static bool IsHeadlessMode => TopLevel.TryGetPlatformHandle()?.HandleDescriptor is null or "STUB"; [NotNull] public static IStorageProvider? StorageProvider { get; internal set; } @@ -114,7 +114,8 @@ public sealed class App : Application public override void OnFrameworkInitializationCompleted() { // Remove DataAnnotations validation plugin since we're using INotifyDataErrorInfo from MvvmToolkit - var dataValidationPluginsToRemove = BindingPlugins.DataValidators + var dataValidationPluginsToRemove = BindingPlugins + .DataValidators .OfType() .ToArray(); @@ -158,19 +159,22 @@ public sealed class App : Application DesktopLifetime.MainWindow = setupWindow; - setupWindow.ShowAsyncCts.Token.Register(() => - { - if (setupWindow.Result == ContentDialogResult.Primary) + setupWindow + .ShowAsyncCts + .Token + .Register(() => { - settingsManager.SetEulaAccepted(); - ShowMainWindow(); - DesktopLifetime.MainWindow.Show(); - } - else - { - Shutdown(); - } - }); + if (setupWindow.Result == ContentDialogResult.Primary) + { + settingsManager.SetEulaAccepted(); + ShowMainWindow(); + DesktopLifetime.MainWindow.Show(); + } + else + { + Shutdown(); + } + }); } else { @@ -219,9 +223,7 @@ public sealed class App : Application mainWindow.Closing += (_, _) => { - var validWindowPosition = mainWindow.Screens.All.Any( - screen => screen.Bounds.Contains(mainWindow.Position) - ); + var validWindowPosition = mainWindow.Screens.All.Any(screen => screen.Bounds.Contains(mainWindow.Position)); settingsManager.Transaction( s => @@ -291,8 +293,7 @@ public sealed class App : Application provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), - provider.GetRequiredService(), - provider.GetRequiredService() + provider.GetRequiredService() }, FooterPages = { provider.GetRequiredService() } } @@ -302,10 +303,7 @@ public sealed class App : Application services.AddSingleton(p => p.GetRequiredService()); } - internal static void ConfigureDialogViewModels( - IServiceCollection services, - Type[] exportedTypes - ) + internal static void ConfigureDialogViewModels(IServiceCollection services, Type[] exportedTypes) { // Dialog factory services.AddSingleton>(provider => @@ -313,17 +311,7 @@ public sealed class App : Application var serviceManager = new ServiceManager(); var serviceManagedTypes = exportedTypes - .Select( - t => - new - { - t, - attributes = t.GetCustomAttributes( - typeof(ManagedServiceAttribute), - true - ) - } - ) + .Select(t => new { t, attributes = t.GetCustomAttributes(typeof(ManagedServiceAttribute), true) }) .Where(t1 => t1.attributes is { Length: > 0 }) .Select(t1 => t1.t) .ToList(); @@ -347,20 +335,18 @@ public sealed class App : Application services.AddMessagePipe(); services.AddMessagePipeNamedPipeInterprocess("StabilityMatrix"); - var exportedTypes = AppDomain.CurrentDomain + var exportedTypes = AppDomain + .CurrentDomain .GetAssemblies() .Where(a => a.FullName?.StartsWith("StabilityMatrix") == true) .SelectMany(a => a.GetExportedTypes()) .ToArray(); var transientTypes = exportedTypes - .Select( - t => new { t, attributes = t.GetCustomAttributes(typeof(TransientAttribute), true) } - ) + .Select(t => new { t, attributes = t.GetCustomAttributes(typeof(TransientAttribute), true) }) .Where( t1 => - t1.attributes is { Length: > 0 } - && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) + t1.attributes is { Length: > 0 } && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) ) .Select(t1 => new { Type = t1.t, Attribute = (TransientAttribute)t1.attributes[0] }); @@ -377,22 +363,12 @@ public sealed class App : Application } var singletonTypes = exportedTypes - .Select( - t => new { t, attributes = t.GetCustomAttributes(typeof(SingletonAttribute), true) } - ) + .Select(t => new { t, attributes = t.GetCustomAttributes(typeof(SingletonAttribute), true) }) .Where( t1 => - t1.attributes is { Length: > 0 } - && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) + t1.attributes is { Length: > 0 } && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) ) - .Select( - t1 => - new - { - Type = t1.t, - Attributes = t1.attributes.Cast().ToArray() - } - ); + .Select(t1 => new { Type = t1.t, Attributes = t1.attributes.Cast().ToArray() }); foreach (var typePair in singletonTypes) { @@ -424,9 +400,7 @@ public sealed class App : Application // Rich presence services.AddSingleton(); - services.AddSingleton( - provider => provider.GetRequiredService() - ); + services.AddSingleton(provider => provider.GetRequiredService()); Config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -472,9 +446,7 @@ public sealed class App : Application jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter()); jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter()); jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter()); - jsonSerializerOptions.Converters.Add( - new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) - ); + jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; var defaultRefitSettings = new RefitSettings @@ -483,8 +455,7 @@ public sealed class App : Application }; // Refit settings for IApiFactory - var defaultSystemTextJsonSettings = - SystemTextJsonContentSerializer.GetDefaultJsonSerializerOptions(); + var defaultSystemTextJsonSettings = SystemTextJsonContentSerializer.GetDefaultJsonSerializerOptions(); defaultSystemTextJsonSettings.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; var apiFactoryRefitSettings = new RefitSettings { @@ -558,27 +529,19 @@ public sealed class App : Application c.BaseAddress = new Uri("https://stableauthentication.azurewebsites.net"); c.Timeout = TimeSpan.FromSeconds(15); }) - .ConfigurePrimaryHttpMessageHandler( - () => new HttpClientHandler { AllowAutoRedirect = false } - ) + .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false }) .AddPolicyHandler(retryPolicy) .AddHttpMessageHandler( serviceProvider => - new TokenAuthHeaderHandler( - serviceProvider.GetRequiredService() - ) + new TokenAuthHeaderHandler(serviceProvider.GetRequiredService()) ); // Add Refit client managers - services - .AddHttpClient("A3Client") - .AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy)); + services.AddHttpClient("A3Client").AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy)); services .AddHttpClient("DontFollowRedirects") - .ConfigurePrimaryHttpMessageHandler( - () => new HttpClientHandler { AllowAutoRedirect = false } - ) + .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false }) .AddPolicyHandler(retryPolicy); /*services.AddHttpClient("IComfyApi") @@ -608,11 +571,7 @@ public sealed class App : Application #if DEBUG builder.AddNLog( ConfigureLogging(), - new NLogProviderOptions - { - IgnoreEmptyEventId = false, - CaptureEventId = EventIdCaptureType.Legacy - } + new NLogProviderOptions { IgnoreEmptyEventId = false, CaptureEventId = EventIdCaptureType.Legacy } ); #else builder.AddNLog(ConfigureLogging()); @@ -645,9 +604,7 @@ public sealed class App : Application var settingsManager = Services.GetRequiredService(); // If RemoveFolderLinksOnShutdown is set, delete all package junctions - if ( - settingsManager is { IsLibraryDirSet: true, Settings.RemoveFolderLinksOnShutdown: true } - ) + if (settingsManager is { IsLibraryDirSet: true, Settings.RemoveFolderLinksOnShutdown: true }) { var sharedFolders = Services.GetRequiredService(); sharedFolders.RemoveLinksForAllPackages(); @@ -689,13 +646,10 @@ public sealed class App : Application .WriteTo( new FileTarget { - Layout = - "${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}", + Layout = "${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}", ArchiveOldFileOnStartup = true, - FileName = - "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.log", - ArchiveFileName = - "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.{#}.log", + FileName = "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.log", + ArchiveFileName = "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.{#}.log", ArchiveNumbering = ArchiveNumberingMode.Rolling, MaxArchiveFiles = 2 } @@ -708,9 +662,7 @@ public sealed class App : Application builder.ForLogger("Microsoft.Extensions.Http.*").WriteToNil(NLog.LogLevel.Warn); // Disable console trace logging by default - builder - .ForLogger("StabilityMatrix.Avalonia.ViewModels.ConsoleViewModel") - .WriteToNil(NLog.LogLevel.Debug); + builder.ForLogger("StabilityMatrix.Avalonia.ViewModels.ConsoleViewModel").WriteToNil(NLog.LogLevel.Debug); builder.ForLogger().FilterMinLevel(NLog.LogLevel.Trace).WriteTo(debugTarget); builder.ForLogger().FilterMinLevel(NLog.LogLevel.Debug).WriteTo(fileTarget); @@ -726,18 +678,20 @@ public sealed class App : Application // Sentry if (SentrySdk.IsEnabled) { - LogManager.Configuration.AddSentry(o => - { - o.InitializeSdk = false; - o.Layout = "${message}"; - o.ShutdownTimeoutSeconds = 5; - o.IncludeEventDataOnBreadcrumbs = true; - o.BreadcrumbLayout = "${logger}: ${message}"; - // Debug and higher are stored as breadcrumbs (default is Info) - o.MinimumBreadcrumbLevel = NLog.LogLevel.Debug; - // Error and higher is sent as event (default is Error) - o.MinimumEventLevel = NLog.LogLevel.Error; - }); + LogManager + .Configuration + .AddSentry(o => + { + o.InitializeSdk = false; + o.Layout = "${message}"; + o.ShutdownTimeoutSeconds = 5; + o.IncludeEventDataOnBreadcrumbs = true; + o.BreadcrumbLayout = "${logger}: ${message}"; + // Debug and higher are stored as breadcrumbs (default is Info) + o.MinimumBreadcrumbLevel = NLog.LogLevel.Debug; + // Error and higher is sent as event (default is Error) + o.MinimumEventLevel = NLog.LogLevel.Error; + }); } LogManager.ReconfigExistingLoggers(); @@ -776,36 +730,34 @@ public sealed class App : Application results.Add(ms); } - Dispatcher.UIThread.InvokeAsync(async () => - { - var dest = await StorageProvider.SaveFilePickerAsync( - new FilePickerSaveOptions() - { - SuggestedFileName = "screenshot.png", - ShowOverwritePrompt = true - } - ); - - if (dest?.TryGetLocalPath() is { } localPath) + Dispatcher + .UIThread + .InvokeAsync(async () => { - var localFile = new FilePath(localPath); - foreach (var (i, stream) in results.Enumerate()) + var dest = await StorageProvider.SaveFilePickerAsync( + new FilePickerSaveOptions() { SuggestedFileName = "screenshot.png", ShowOverwritePrompt = true } + ); + + if (dest?.TryGetLocalPath() is { } localPath) { - var name = localFile.NameWithoutExtension; - if (results.Count > 1) + var localFile = new FilePath(localPath); + foreach (var (i, stream) in results.Enumerate()) { - name += $"_{i + 1}"; - } + var name = localFile.NameWithoutExtension; + if (results.Count > 1) + { + name += $"_{i + 1}"; + } - localFile = localFile.Directory!.JoinFile(name + ".png"); - localFile.Create(); + localFile = localFile.Directory!.JoinFile(name + ".png"); + localFile.Create(); - await using var fileStream = localFile.Info.OpenWrite(); - stream.Seek(0, SeekOrigin.Begin); - await stream.CopyToAsync(fileStream); + await using var fileStream = localFile.Info.OpenWrite(); + stream.Seek(0, SeekOrigin.Begin); + await stream.CopyToAsync(fileStream); + } } - } - }); + }); } [Conditional("DEBUG")] @@ -821,8 +773,7 @@ public sealed class App : Application { #if DEBUG setupBuilder.SetupExtensions( - extensionBuilder => - extensionBuilder.RegisterTarget("DataStoreLogger") + extensionBuilder => extensionBuilder.RegisterTarget("DataStoreLogger") ); #endif } diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/TabViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/TabViewModelBase.cs index a3d774eb..97012d14 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/TabViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/TabViewModelBase.cs @@ -1,13 +1,6 @@ -using System.Collections.Generic; -using CommunityToolkit.Mvvm.ComponentModel; -using FluentAvalonia.UI.Controls; +namespace StabilityMatrix.Avalonia.ViewModels.Base; -namespace StabilityMatrix.Avalonia.ViewModels.Base; - -public abstract partial class TabViewModelBase : ViewModelBase +public abstract class TabViewModelBase : ViewModelBase { - [ObservableProperty] - private List primaryCommands = new(); - public abstract string Header { get; } } diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs index fb9d4d50..324642b1 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs @@ -40,9 +40,8 @@ public partial class HuggingFacePageViewModel : TabViewModelBase private readonly ITrackedDownloadService trackedDownloadService; private readonly ISettingsManager settingsManager; private readonly INotificationService notificationService; - - public SourceCache ItemsCache { get; } = - new(i => i.RepositoryPath + i.ModelName); + + public SourceCache ItemsCache { get; } = new(i => i.RepositoryPath + i.ModelName); public IObservableCollection Categories { get; set; } = new ObservableCollectionExtended(); @@ -61,8 +60,7 @@ public partial class HuggingFacePageViewModel : TabViewModelBase [NotifyPropertyChangedFor(nameof(DownloadPercentText))] private ProgressReport totalProgress; - private readonly DispatcherTimer progressTimer = - new() { Interval = TimeSpan.FromMilliseconds(100) }; + private readonly DispatcherTimer progressTimer = new() { Interval = TimeSpan.FromMilliseconds(100) }; public HuggingFacePageViewModel( ITrackedDownloadService trackedDownloadService, @@ -78,13 +76,7 @@ public partial class HuggingFacePageViewModel : TabViewModelBase .Connect() .DeferUntilLoaded() .Group(i => i.ModelCategory) - .Transform( - g => - new CategoryViewModel(g.Cache.Items) - { - Title = g.Key.GetDescription() ?? g.Key.ToString() - } - ) + .Transform(g => new CategoryViewModel(g.Cache.Items) { Title = g.Key.GetDescription() ?? g.Key.ToString() }) .SortBy(vm => vm.Title) .Bind(Categories) .WhenAnyPropertyChanged() @@ -102,7 +94,7 @@ public partial class HuggingFacePageViewModel : TabViewModelBase TotalProgress = new ProgressReport(current: currentSum, total: totalSum); }; - + using var reader = new StreamReader(Assets.HfPackagesJson.Open()); var packages = JsonSerializer.Deserialize>( @@ -111,15 +103,6 @@ public partial class HuggingFacePageViewModel : TabViewModelBase ) ?? throw new InvalidOperationException("Failed to read hf-packages.json"); ItemsCache.EditDiff(packages, (a, b) => a.RepositoryPath == b.RepositoryPath); - - var btn = new CommandBarButton(); - var obs = btn.GetObservable(InputElement.IsEnabledProperty) - .Select(_ => NumSelected > 0); - btn.Bind(InputElement.IsEnabledProperty, obs); - btn.Command = ImportSelectedCommand; - btn.Foreground = new SolidColorBrush(Colors.Lime); - btn.IconSource = new SymbolIconSource { Symbol = Symbol.Download }; - PrimaryCommands.Add(btn); } public void ClearSelection() @@ -148,8 +131,7 @@ public partial class HuggingFacePageViewModel : TabViewModelBase { foreach (var file in viewModel.Item.Files) { - var url = - $"https://huggingface.co/{viewModel.Item.RepositoryPath}/resolve/main/{file}?download=true"; + var url = $"https://huggingface.co/{viewModel.Item.RepositoryPath}/resolve/main/{file}?download=true"; var sharedFolderType = viewModel.Item.ModelCategory.ConvertTo(); var downloadPath = new FilePath( Path.Combine( From 81d0a470e1970d652471a7fc2b38deea0c6cdd5a Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 2 Dec 2023 18:21:21 -0800 Subject: [PATCH 060/166] Maybe fix settings resetting itself? --- .../HuggingFacePageViewModel.cs | 6 ++ .../Views/HuggingFacePage.axaml | 2 +- .../Services/SettingsManager.cs | 61 ++++++------------- 3 files changed, 26 insertions(+), 43 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs index 324642b1..0916243d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs @@ -94,6 +94,12 @@ public partial class HuggingFacePageViewModel : TabViewModelBase TotalProgress = new ProgressReport(current: currentSum, total: totalSum); }; + } + + public override void OnLoaded() + { + if (ItemsCache.Count > 0) + return; using var reader = new StreamReader(Assets.HfPackagesJson.Open()); var packages = diff --git a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml index 3ee7a404..2e222757 100644 --- a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml +++ b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml @@ -44,7 +44,7 @@ IsEnabled="{Binding !!NumSelected}" IconSource="Download" Foreground="{DynamicResource AccentButtonBackground}" - Command="{Binding ImportSelected}" + Command="{Binding ImportSelectedCommand}" Label="{x:Static lang:Resources.Action_Import}" /> Path.Combine(Compat.AppDataHome, "global.json"); @@ -107,9 +108,7 @@ public class SettingsManager : ISettingsManager { if (!IsLibraryDirSet) { - throw new InvalidOperationException( - "LibraryDir not set when BeginTransaction was called" - ); + throw new InvalidOperationException("LibraryDir not set when BeginTransaction was called"); } return new SettingsTransaction(this, SaveSettingsAsync); } @@ -135,9 +134,7 @@ public class SettingsManager : ISettingsManager { if (expression.Body is not MemberExpression memberExpression) { - throw new ArgumentException( - $"Expression must be a member expression, not {expression.Body.NodeType}" - ); + throw new ArgumentException($"Expression must be a member expression, not {expression.Body.NodeType}"); } var propertyInfo = memberExpression.Member as PropertyInfo; @@ -188,8 +185,7 @@ public class SettingsManager : ISettingsManager if (args.IsRelay && ReferenceEquals(sender, source)) return; Logger.Trace( - "[RelayPropertyFor] " - + "Settings.{TargetProperty:l} -> {SourceType:l}.{SourceProperty:l}", + "[RelayPropertyFor] " + "Settings.{TargetProperty:l} -> {SourceType:l}.{SourceProperty:l}", targetPropertyName, sourceTypeName, propertyName @@ -205,8 +201,7 @@ public class SettingsManager : ISettingsManager return; Logger.Trace( - "[RelayPropertyFor] " - + "{SourceType:l}.{SourceProperty:l} -> Settings.{TargetProperty:l}", + "[RelayPropertyFor] " + "{SourceType:l}.{SourceProperty:l} -> Settings.{TargetProperty:l}", sourceTypeName, propertyName, targetPropertyName @@ -231,10 +226,7 @@ public class SettingsManager : ISettingsManager } // Invoke property changed event, passing along sender - SettingsPropertyChanged?.Invoke( - sender, - new RelayPropertyChangedEventArgs(targetPropertyName, true) - ); + SettingsPropertyChanged?.Invoke(sender, new RelayPropertyChangedEventArgs(targetPropertyName, true)); }; // Set initial value if requested @@ -339,10 +331,7 @@ public class SettingsManager : ISettingsManager var libraryJsonFile = Compat.AppDataHome.JoinFile("library.json"); var library = new LibrarySettings { LibraryPath = path }; - var libraryJson = JsonSerializer.Serialize( - library, - new JsonSerializerOptions { WriteIndented = true } - ); + var libraryJson = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true }); libraryJsonFile.WriteAllText(libraryJson); // actually create the LibraryPath directory @@ -464,9 +453,7 @@ public class SettingsManager : ISettingsManager public void SetLastUpdateCheck(InstalledPackage package) { - var installedPackage = Settings.InstalledPackages.First( - p => p.DisplayName == package.DisplayName - ); + var installedPackage = Settings.InstalledPackages.First(p => p.DisplayName == package.DisplayName); installedPackage.LastUpdateCheck = package.LastUpdateCheck; installedPackage.UpdateAvailable = package.UpdateAvailable; SaveSettings(); @@ -494,14 +481,10 @@ public class SettingsManager : ISettingsManager public string? GetActivePackageHost() { - var package = Settings.InstalledPackages.FirstOrDefault( - x => x.Id == Settings.ActiveInstalledPackageId - ); + var package = Settings.InstalledPackages.FirstOrDefault(x => x.Id == Settings.ActiveInstalledPackageId); if (package == null) return null; - var hostOption = package.LaunchArgs?.FirstOrDefault( - x => x.Name.ToLowerInvariant() == "host" - ); + var hostOption = package.LaunchArgs?.FirstOrDefault(x => x.Name.ToLowerInvariant() == "host"); if (hostOption?.OptionValue != null) { return hostOption.OptionValue as string; @@ -511,14 +494,10 @@ public class SettingsManager : ISettingsManager public string? GetActivePackagePort() { - var package = Settings.InstalledPackages.FirstOrDefault( - x => x.Id == Settings.ActiveInstalledPackageId - ); + var package = Settings.InstalledPackages.FirstOrDefault(x => x.Id == Settings.ActiveInstalledPackageId); if (package == null) return null; - var portOption = package.LaunchArgs?.FirstOrDefault( - x => x.Name.ToLowerInvariant() == "port" - ); + var portOption = package.LaunchArgs?.FirstOrDefault(x => x.Name.ToLowerInvariant() == "port"); if (portOption?.OptionValue != null) { return portOption.OptionValue as string; @@ -627,6 +606,7 @@ public class SettingsManager : ISettingsManager settingsFile.WriteAllText(settingsJson); Loaded?.Invoke(this, EventArgs.Empty); + isLoaded = true; return; } @@ -639,14 +619,11 @@ public class SettingsManager : ISettingsManager } if ( - JsonSerializer.Deserialize( - fileStream, - SettingsSerializerContext.Default.Settings - ) is - { } loadedSettings + JsonSerializer.Deserialize(fileStream, SettingsSerializerContext.Default.Settings) is { } loadedSettings ) { Settings = loadedSettings; + isLoaded = true; } Loaded?.Invoke(this, EventArgs.Empty); @@ -670,10 +647,10 @@ public class SettingsManager : ISettingsManager settingsFile.Create(); } - var jsonBytes = JsonSerializer.SerializeToUtf8Bytes( - Settings, - SettingsSerializerContext.Default.Settings - ); + if (!isLoaded) + return; + + var jsonBytes = JsonSerializer.SerializeToUtf8Bytes(Settings, SettingsSerializerContext.Default.Settings); File.WriteAllBytes(SettingsPath, jsonBytes); } From bf2a02b5ef7ad206e4dc91c0f0d16aa30db35981 Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 2 Dec 2023 19:17:32 -0800 Subject: [PATCH 061/166] fix drag & drop targets not showing and clean up page selector for civit browser --- .../Languages/Resources.Designer.cs | 9 ++ .../Languages/Resources.es.resx | 3 + .../Languages/Resources.fr-FR.resx | 3 + .../Languages/Resources.it-it.resx | 3 + .../Languages/Resources.ja-JP.resx | 3 + .../Languages/Resources.resx | 3 + .../Languages/Resources.ru-ru.resx | 3 + .../Languages/Resources.zh-Hans.resx | 3 + .../Languages/Resources.zh-Hant.resx | 3 + .../CheckpointManager/CheckpointFile.cs | 69 +++----------- .../Views/CheckpointsPage.axaml.cs | 25 +++-- .../Views/CivitAiBrowserPage.axaml | 93 +++++++++---------- 12 files changed, 107 insertions(+), 113 deletions(-) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index 481fceb6..d6961696 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -788,6 +788,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to CivitAI. + /// + public static string Label_CivitAi { + get { + return ResourceManager.GetString("Label_CivitAi", resourceCulture); + } + } + /// /// Looks up a localized string similar to You must be logged in to download this checkpoint. Please enter a CivitAI API Key in the settings.. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.es.resx b/StabilityMatrix.Avalonia/Languages/Resources.es.resx index 3b586761..46da9160 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.es.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.es.resx @@ -680,4 +680,7 @@ Restablecer Diseño Predeterminado + + CivitAI + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx b/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx index 8623c810..574d80e7 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.fr-FR.resx @@ -678,4 +678,7 @@ Rétablir la présentation par défaut + + CivitAI + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.it-it.resx b/StabilityMatrix.Avalonia/Languages/Resources.it-it.resx index cadda8ef..dd3bcc3b 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.it-it.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.it-it.resx @@ -680,4 +680,7 @@ + + CivitAI + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.ja-JP.resx b/StabilityMatrix.Avalonia/Languages/Resources.ja-JP.resx index 63d37d58..4c5c7495 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.ja-JP.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.ja-JP.resx @@ -680,4 +680,7 @@ レイアウトを初期状態に戻す + + CivitAI + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index ea10f569..0565f91f 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -852,4 +852,7 @@ System Information + + CivitAI + diff --git a/StabilityMatrix.Avalonia/Languages/Resources.ru-ru.resx b/StabilityMatrix.Avalonia/Languages/Resources.ru-ru.resx index 149c0a0b..24716a61 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.ru-ru.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.ru-ru.resx @@ -678,4 +678,7 @@ Восстановить вид по умолчанию + + CivitAI + \ No newline at end of file diff --git a/StabilityMatrix.Avalonia/Languages/Resources.zh-Hans.resx b/StabilityMatrix.Avalonia/Languages/Resources.zh-Hans.resx index 99c38fb7..46957dca 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.zh-Hans.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.zh-Hans.resx @@ -647,4 +647,7 @@ 分支 + + CivitAI + diff --git a/StabilityMatrix.Avalonia/Languages/Resources.zh-Hant.resx b/StabilityMatrix.Avalonia/Languages/Resources.zh-Hant.resx index 49df915d..1ea14f2f 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.zh-Hant.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.zh-Hant.resx @@ -639,4 +639,7 @@ 分支 + + CivitAI + diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs index 0eb0865f..333a3a02 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs @@ -70,15 +70,8 @@ public partial class CheckpointFile : ViewModelBase public ObservableCollection Badges { get; set; } = new(); - public static readonly string[] SupportedCheckpointExtensions = - { - ".safetensors", - ".pt", - ".ckpt", - ".pth", - ".bin" - }; - private static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" }; + public static readonly string[] SupportedCheckpointExtensions = { ".safetensors", ".pt", ".ckpt", ".pth", ".bin" }; + private static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".gif" }; private static readonly string[] SupportedMetadataExtensions = { ".json" }; partial void OnConnectedModelChanged(ConnectedModelInfo? value) @@ -102,9 +95,7 @@ public partial class CheckpointFile : ViewModelBase { if (string.IsNullOrEmpty(FilePath)) { - throw new InvalidOperationException( - "Cannot get connected model info file path when FilePath is empty" - ); + throw new InvalidOperationException("Cannot get connected model info file path when FilePath is empty"); } var modelNameNoExt = Path.GetFileNameWithoutExtension((string?)FilePath); var modelDir = Path.GetDirectoryName((string?)FilePath) ?? ""; @@ -201,10 +192,7 @@ public partial class CheckpointFile : ViewModelBase var cmInfoPath = Path.Combine(parentPath, $"{originalNameNoExt}.cm-info.json"); if (File.Exists(cmInfoPath)) { - File.Move( - cmInfoPath, - Path.Combine(parentPath, $"{nameNoExt}.cm-info.json") - ); + File.Move(cmInfoPath, Path.Combine(parentPath, $"{nameNoExt}.cm-info.json")); } } } @@ -239,10 +227,7 @@ public partial class CheckpointFile : ViewModelBase [RelayCommand] private async Task FindConnectedMetadata(bool forceReimport = false) { - if ( - App.Services.GetService(typeof(IMetadataImportService)) - is not IMetadataImportService importService - ) + if (App.Services.GetService(typeof(IMetadataImportService)) is not IMetadataImportService importService) return; IsLoading = true; @@ -254,11 +239,7 @@ public partial class CheckpointFile : ViewModelBase Progress = report; }); - var cmInfo = await importService.GetMetadataForFile( - FilePath, - progressReport, - forceReimport - ); + var cmInfo = await importService.GetMetadataForFile(FilePath, progressReport, forceReimport); if (cmInfo != null) { ConnectedModel = cmInfo; @@ -297,9 +278,7 @@ public partial class CheckpointFile : ViewModelBase { if ( !SupportedCheckpointExtensions.Any( - ext => - Path.GetExtension(file) - .Equals(ext, StringComparison.InvariantCultureIgnoreCase) + ext => Path.GetExtension(file).Equals(ext, StringComparison.InvariantCultureIgnoreCase) ) ) continue; @@ -310,10 +289,7 @@ public partial class CheckpointFile : ViewModelBase FilePath = Path.Combine(directory, file), }; - var jsonPath = Path.Combine( - directory, - $"{Path.GetFileNameWithoutExtension(file)}.cm-info.json" - ); + var jsonPath = Path.Combine(directory, $"{Path.GetFileNameWithoutExtension(file)}.cm-info.json"); if (File.Exists(jsonPath)) { var json = File.ReadAllText(jsonPath); @@ -322,13 +298,7 @@ public partial class CheckpointFile : ViewModelBase } checkpointFile.PreviewImagePath = SupportedImageExtensions - .Select( - ext => - Path.Combine( - directory, - $"{Path.GetFileNameWithoutExtension(file)}.preview{ext}" - ) - ) + .Select(ext => Path.Combine(directory, $"{Path.GetFileNameWithoutExtension(file)}.preview{ext}")) .Where(File.Exists) .FirstOrDefault(); @@ -345,28 +315,16 @@ public partial class CheckpointFile : ViewModelBase public static IEnumerable GetAllCheckpointFiles(string modelsDirectory) { - foreach ( - var file in Directory.EnumerateFiles( - modelsDirectory, - "*.*", - SearchOption.AllDirectories - ) - ) + foreach (var file in Directory.EnumerateFiles(modelsDirectory, "*.*", SearchOption.AllDirectories)) { if ( !SupportedCheckpointExtensions.Any( - ext => - Path.GetExtension(file) - .Equals(ext, StringComparison.InvariantCultureIgnoreCase) + ext => Path.GetExtension(file).Equals(ext, StringComparison.InvariantCultureIgnoreCase) ) ) continue; - var checkpointFile = new CheckpointFile - { - Title = Path.GetFileNameWithoutExtension(file), - FilePath = file - }; + var checkpointFile = new CheckpointFile { Title = Path.GetFileNameWithoutExtension(file), FilePath = file }; var jsonPath = Path.Combine( Path.GetDirectoryName(file) ?? "", @@ -474,6 +432,5 @@ public partial class CheckpointFile : ViewModelBase } } - public static IEqualityComparer FilePathComparer { get; } = - new FilePathEqualityComparer(); + public static IEqualityComparer FilePathComparer { get; } = new FilePathEqualityComparer(); } diff --git a/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs b/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs index 34fcc7aa..b7aec886 100644 --- a/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs @@ -44,8 +44,7 @@ public partial class CheckpointsPage : UserControlBase if (DataContext is CheckpointsPageViewModel vm) { - subscription = vm.WhenPropertyChanged(m => m.ShowConnectedModelImages) - .Subscribe(_ => InvalidateRepeater()); + subscription = vm.WhenPropertyChanged(m => m.ShowConnectedModelImages).Subscribe(_ => InvalidateRepeater()); } } @@ -98,9 +97,14 @@ public partial class CheckpointsPage : UserControlBase private static void OnDragExit(object? sender, DragEventArgs e) { var sourceDataContext = (e.Source as Control)?.DataContext; - if (sourceDataContext is CheckpointFolder folder) + switch (sourceDataContext) { - folder.IsCurrentDragTarget = false; + case CheckpointFolder folder: + folder.IsCurrentDragTarget = false; + break; + case CheckpointFile file: + file.ParentFolder.IsCurrentDragTarget = false; + break; } } @@ -123,7 +127,10 @@ public partial class CheckpointsPage : UserControlBase { folder.IsExpanded = true; if (e.Data.Get("Context") is not CheckpointFile file) - return; + { + folder.IsCurrentDragTarget = true; + break; + } var filePath = new FilePath(file.FilePath); folder.IsCurrentDragTarget = filePath.Directory?.FullPath != folder.DirectoryPath; @@ -132,12 +139,14 @@ public partial class CheckpointsPage : UserControlBase case CheckpointFile file: { if (e.Data.Get("Context") is not CheckpointFile dragFile) - return; + { + file.ParentFolder.IsCurrentDragTarget = true; + break; + } var parentFolder = file.ParentFolder; var dragFilePath = new FilePath(dragFile.FilePath); - parentFolder.IsCurrentDragTarget = - dragFilePath.Directory?.FullPath != parentFolder.DirectoryPath; + parentFolder.IsCurrentDragTarget = dragFilePath.Directory?.FullPath != parentFolder.DirectoryPath; break; } } diff --git a/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml index 36eccf9f..f61fc605 100644 --- a/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml +++ b/StabilityMatrix.Avalonia/Views/CivitAiBrowserPage.axaml @@ -462,57 +462,52 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + Date: Sat, 2 Dec 2023 20:29:56 -0800 Subject: [PATCH 062/166] add model browser tab titles to resources --- StabilityMatrix.Avalonia/Languages/Resources.Designer.cs | 9 +++++++++ StabilityMatrix.Avalonia/Languages/Resources.resx | 3 +++ .../CheckpointBrowser/CivitAiBrowserViewModel.cs | 3 ++- .../CheckpointBrowser/HuggingFacePageViewModel.cs | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index d6961696..fb9073e0 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -1157,6 +1157,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Hugging Face. + /// + public static string Label_HuggingFace { + get { + return ResourceManager.GetString("Label_HuggingFace", resourceCulture); + } + } + /// /// Looks up a localized string similar to Image to Image. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index 0565f91f..dfc94f48 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -855,4 +855,7 @@ CivitAI + + Hugging Face + diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs index ef2d587a..fabcea75 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs @@ -19,6 +19,7 @@ using LiteDB; using LiteDB.Async; using NLog; using Refit; +using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.CheckpointManager; @@ -607,5 +608,5 @@ public partial class CivitAiBrowserViewModel : TabViewModelBase allModelCards.Count > 0 ? $"{allModelCards.Count} results hidden by filters" : "No results found"; } - public override string Header => "Civitai"; + public override string Header => Resources.Label_CivitAi; } diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs index 0916243d..1930907d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/HuggingFacePageViewModel.cs @@ -189,5 +189,5 @@ public partial class HuggingFacePageViewModel : TabViewModelBase }); } - public override string Header => "Hugging Face"; + public override string Header => Resources.Label_HuggingFace; } From 39c22a242412d7086ac1a70a92527315948b40ff Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 12:36:29 -0500 Subject: [PATCH 063/166] Fix ControlNet not applying to refiner --- .../Inference/ModuleApplyStepEventArgs.cs | 4 +- .../Inference/Modules/ControlNetModule.cs | 42 +++++++++++++++---- .../Inference/SamplerCardViewModel.cs | 12 +++--- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs b/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs index 0e23ee17..f90baa4b 100644 --- a/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs +++ b/StabilityMatrix.Avalonia/Models/Inference/ModuleApplyStepEventArgs.cs @@ -40,7 +40,7 @@ public class ModuleApplyStepEventArgs : EventArgs public ( ConditioningNodeConnection Positive, ConditioningNodeConnection Negative - ) Conditioning { get; set; } + )? Conditioning { get; set; } /// /// Temporary refiner conditioning apply step, used by samplers to apply control net. @@ -48,7 +48,7 @@ public class ModuleApplyStepEventArgs : EventArgs public ( ConditioningNodeConnection Positive, ConditioningNodeConnection Negative - ) RefinerConditioning { get; set; } + )? RefinerConditioning { get; set; } /// /// Temporary model apply step, used by samplers to apply control net. diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs index 20c5973f..4f4e7191 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ControlNetModule.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using StabilityMatrix.Avalonia.Controls; @@ -23,7 +24,7 @@ public class ControlNetModule : ModuleBase AddCards(vmFactory.Get()); } - public IEnumerable GetInputImages() + protected override IEnumerable GetInputImages() { if (GetCard().SelectImageCardViewModel.ImageSource is { } image) { @@ -43,7 +44,7 @@ public class ControlNetModule : ModuleBase Image = card.SelectImageCardViewModel.ImageSource?.GetHashGuidFileNameCached( "Inference" - ) ?? throw new ValidationException() + ) ?? throw new ValidationException("No ImageSource") } ); @@ -51,18 +52,22 @@ public class ControlNetModule : ModuleBase new ComfyNodeBuilder.ControlNetLoader { Name = e.Nodes.GetUniqueName("ControlNetLoader"), - ControlNetName = card.SelectedModel?.FileName ?? throw new ValidationException(), + ControlNetName = + card.SelectedModel?.FileName + ?? throw new ValidationException("No SelectedModel"), } ); var controlNetApply = e.Nodes.AddTypedNode( new ComfyNodeBuilder.ControlNetApplyAdvanced { - Name = e.Nodes.GetUniqueName("ControlNet"), + Name = e.Nodes.GetUniqueName("ControlNetApply"), Image = imageLoad.Output1, ControlNet = controlNetLoader.Output, - Positive = e.Temp.Conditioning.Positive, - Negative = e.Temp.Conditioning.Negative, + Positive = + e.Temp.Conditioning?.Positive ?? throw new ArgumentException("No Conditioning"), + Negative = + e.Temp.Conditioning?.Negative ?? throw new ArgumentException("No Conditioning"), Strength = card.Strength, StartPercent = card.StartPercent, EndPercent = card.EndPercent, @@ -70,5 +75,28 @@ public class ControlNetModule : ModuleBase ); e.Temp.Conditioning = (controlNetApply.Output1, controlNetApply.Output2); + + // Refiner if available + if (e.Temp.RefinerConditioning is not null) + { + var controlNetRefinerApply = e.Nodes.AddTypedNode( + new ComfyNodeBuilder.ControlNetApplyAdvanced + { + Name = e.Nodes.GetUniqueName("Refiner_ControlNetApply"), + Image = imageLoad.Output1, + ControlNet = controlNetLoader.Output, + Positive = e.Temp.RefinerConditioning.Value.Positive, + Negative = e.Temp.RefinerConditioning.Value.Negative, + Strength = card.Strength, + StartPercent = card.StartPercent, + EndPercent = card.EndPercent, + } + ); + + e.Temp.RefinerConditioning = ( + controlNetRefinerApply.Output1, + controlNetRefinerApply.Output2 + ); + } } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs index bec67ed0..eb975d24 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs @@ -167,8 +167,8 @@ public partial class SamplerCardViewModel Scheduler = e.Builder.Connections.PrimaryScheduler?.Name!, Steps = Steps, Cfg = CfgScale, - Positive = e.Temp.Conditioning.Positive, - Negative = e.Temp.Conditioning.Negative, + Positive = e.Temp.Conditioning?.Positive!, + Negative = e.Temp.Conditioning?.Negative!, LatentImage = primaryLatent, Denoise = DenoiseStrength, } @@ -192,8 +192,8 @@ public partial class SamplerCardViewModel Cfg = CfgScale, SamplerName = e.Builder.Connections.PrimarySampler?.Name!, Scheduler = e.Builder.Connections.PrimaryScheduler?.Name!, - Positive = e.Temp.Conditioning.Positive, - Negative = e.Temp.Conditioning.Negative, + Positive = e.Temp.Conditioning?.Positive!, + Negative = e.Temp.Conditioning?.Negative!, LatentImage = primaryLatent, StartAtStep = 0, EndAtStep = Steps, @@ -215,8 +215,8 @@ public partial class SamplerCardViewModel Cfg = CfgScale, SamplerName = e.Builder.Connections.PrimarySampler?.Name!, Scheduler = e.Builder.Connections.PrimaryScheduler?.Name!, - Positive = e.Temp.RefinerConditioning.Positive, - Negative = e.Temp.RefinerConditioning.Negative, + Positive = e.Temp.RefinerConditioning?.Positive!, + Negative = e.Temp.RefinerConditioning?.Negative!, // Connect to previous sampler LatentImage = sampler.Output, StartAtStep = Steps, From 6a1883c6f8a7b65e28a7144698c7297e89d4f818 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 14:16:49 -0500 Subject: [PATCH 064/166] Filter LoadableViewModelBase to debug and above --- StabilityMatrix.Avalonia/App.axaml.cs | 217 ++++++++++---------------- 1 file changed, 86 insertions(+), 131 deletions(-) diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index d5e91657..238acca3 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -1,7 +1,3 @@ -#if DEBUG -using StabilityMatrix.Avalonia.Diagnostics.LogViewer; -using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions; -#endif using System; using System.Collections.Generic; using System.Diagnostics; @@ -64,6 +60,11 @@ using StabilityMatrix.Core.Services; using Application = Avalonia.Application; using DrawingColor = System.Drawing.Color; using LogLevel = Microsoft.Extensions.Logging.LogLevel; +#if DEBUG +using StabilityMatrix.Avalonia.Diagnostics.LogViewer; +using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions; +#endif + namespace StabilityMatrix.Avalonia; @@ -77,8 +78,7 @@ public sealed class App : Application public static TopLevel TopLevel => TopLevel.GetTopLevel(VisualRoot)!; - internal static bool IsHeadlessMode => - TopLevel.TryGetPlatformHandle()?.HandleDescriptor is null or "STUB"; + internal static bool IsHeadlessMode => TopLevel.TryGetPlatformHandle()?.HandleDescriptor is null or "STUB"; [NotNull] public static IStorageProvider? StorageProvider { get; internal set; } @@ -114,7 +114,8 @@ public sealed class App : Application public override void OnFrameworkInitializationCompleted() { // Remove DataAnnotations validation plugin since we're using INotifyDataErrorInfo from MvvmToolkit - var dataValidationPluginsToRemove = BindingPlugins.DataValidators + var dataValidationPluginsToRemove = BindingPlugins + .DataValidators .OfType() .ToArray(); @@ -158,19 +159,22 @@ public sealed class App : Application DesktopLifetime.MainWindow = setupWindow; - setupWindow.ShowAsyncCts.Token.Register(() => - { - if (setupWindow.Result == ContentDialogResult.Primary) + setupWindow + .ShowAsyncCts + .Token + .Register(() => { - settingsManager.SetEulaAccepted(); - ShowMainWindow(); - DesktopLifetime.MainWindow.Show(); - } - else - { - Shutdown(); - } - }); + if (setupWindow.Result == ContentDialogResult.Primary) + { + settingsManager.SetEulaAccepted(); + ShowMainWindow(); + DesktopLifetime.MainWindow.Show(); + } + else + { + Shutdown(); + } + }); } else { @@ -219,9 +223,7 @@ public sealed class App : Application mainWindow.Closing += (_, _) => { - var validWindowPosition = mainWindow.Screens.All.Any( - screen => screen.Bounds.Contains(mainWindow.Position) - ); + var validWindowPosition = mainWindow.Screens.All.Any(screen => screen.Bounds.Contains(mainWindow.Position)); settingsManager.Transaction( s => @@ -301,10 +303,7 @@ public sealed class App : Application services.AddSingleton(p => p.GetRequiredService()); } - internal static void ConfigureDialogViewModels( - IServiceCollection services, - Type[] exportedTypes - ) + internal static void ConfigureDialogViewModels(IServiceCollection services, Type[] exportedTypes) { // Dialog factory services.AddSingleton>(provider => @@ -312,17 +311,7 @@ public sealed class App : Application var serviceManager = new ServiceManager(); var serviceManagedTypes = exportedTypes - .Select( - t => - new - { - t, - attributes = t.GetCustomAttributes( - typeof(ManagedServiceAttribute), - true - ) - } - ) + .Select(t => new { t, attributes = t.GetCustomAttributes(typeof(ManagedServiceAttribute), true) }) .Where(t1 => t1.attributes is { Length: > 0 }) .Select(t1 => t1.t) .ToList(); @@ -346,21 +335,18 @@ public sealed class App : Application services.AddMessagePipe(); services.AddMessagePipeNamedPipeInterprocess("StabilityMatrix"); - var exportedTypes = AppDomain.CurrentDomain + var exportedTypes = AppDomain + .CurrentDomain .GetAssemblies() .Where(a => a.FullName?.StartsWith("StabilityMatrix") == true) .SelectMany(a => a.GetExportedTypes()) .ToArray(); var transientTypes = exportedTypes - .Select( - t => - new { t, attributes = t.GetCustomAttributes(typeof(TransientAttribute), false) } - ) + .Select(t => new { t, attributes = t.GetCustomAttributes(typeof(TransientAttribute), false) }) .Where( t1 => - t1.attributes is { Length: > 0 } - && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) + t1.attributes is { Length: > 0 } && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) ) .Select(t1 => new { Type = t1.t, Attribute = (TransientAttribute)t1.attributes[0] }); @@ -377,23 +363,12 @@ public sealed class App : Application } var singletonTypes = exportedTypes - .Select( - t => - new { t, attributes = t.GetCustomAttributes(typeof(SingletonAttribute), false) } - ) + .Select(t => new { t, attributes = t.GetCustomAttributes(typeof(SingletonAttribute), false) }) .Where( t1 => - t1.attributes is { Length: > 0 } - && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) + t1.attributes is { Length: > 0 } && !t1.t.Name.Contains("Mock", StringComparison.OrdinalIgnoreCase) ) - .Select( - t1 => - new - { - Type = t1.t, - Attributes = t1.attributes.Cast().ToArray() - } - ); + .Select(t1 => new { Type = t1.t, Attributes = t1.attributes.Cast().ToArray() }); foreach (var typePair in singletonTypes) { @@ -425,9 +400,7 @@ public sealed class App : Application // Rich presence services.AddSingleton(); - services.AddSingleton( - provider => provider.GetRequiredService() - ); + services.AddSingleton(provider => provider.GetRequiredService()); Config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) @@ -473,9 +446,7 @@ public sealed class App : Application jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter()); jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter()); jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter()); - jsonSerializerOptions.Converters.Add( - new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) - ); + jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; var defaultRefitSettings = new RefitSettings @@ -484,8 +455,7 @@ public sealed class App : Application }; // Refit settings for IApiFactory - var defaultSystemTextJsonSettings = - SystemTextJsonContentSerializer.GetDefaultJsonSerializerOptions(); + var defaultSystemTextJsonSettings = SystemTextJsonContentSerializer.GetDefaultJsonSerializerOptions(); defaultSystemTextJsonSettings.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; var apiFactoryRefitSettings = new RefitSettings { @@ -559,27 +529,19 @@ public sealed class App : Application c.BaseAddress = new Uri("https://stableauthentication.azurewebsites.net"); c.Timeout = TimeSpan.FromSeconds(15); }) - .ConfigurePrimaryHttpMessageHandler( - () => new HttpClientHandler { AllowAutoRedirect = false } - ) + .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false }) .AddPolicyHandler(retryPolicy) .AddHttpMessageHandler( serviceProvider => - new TokenAuthHeaderHandler( - serviceProvider.GetRequiredService() - ) + new TokenAuthHeaderHandler(serviceProvider.GetRequiredService()) ); // Add Refit client managers - services - .AddHttpClient("A3Client") - .AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy)); + services.AddHttpClient("A3Client").AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy)); services .AddHttpClient("DontFollowRedirects") - .ConfigurePrimaryHttpMessageHandler( - () => new HttpClientHandler { AllowAutoRedirect = false } - ) + .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { AllowAutoRedirect = false }) .AddPolicyHandler(retryPolicy); /*services.AddHttpClient("IComfyApi") @@ -609,11 +571,7 @@ public sealed class App : Application #if DEBUG builder.AddNLog( ConfigureLogging(), - new NLogProviderOptions - { - IgnoreEmptyEventId = false, - CaptureEventId = EventIdCaptureType.Legacy - } + new NLogProviderOptions { IgnoreEmptyEventId = false, CaptureEventId = EventIdCaptureType.Legacy } ); #else builder.AddNLog(ConfigureLogging()); @@ -646,9 +604,7 @@ public sealed class App : Application var settingsManager = Services.GetRequiredService(); // If RemoveFolderLinksOnShutdown is set, delete all package junctions - if ( - settingsManager is { IsLibraryDirSet: true, Settings.RemoveFolderLinksOnShutdown: true } - ) + if (settingsManager is { IsLibraryDirSet: true, Settings.RemoveFolderLinksOnShutdown: true }) { var sharedFolders = Services.GetRequiredService(); sharedFolders.RemoveLinksForAllPackages(); @@ -690,13 +646,10 @@ public sealed class App : Application .WriteTo( new FileTarget { - Layout = - "${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}", + Layout = "${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true}", ArchiveOldFileOnStartup = true, - FileName = - "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.log", - ArchiveFileName = - "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.{#}.log", + FileName = "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.log", + ArchiveFileName = "${specialfolder:folder=ApplicationData}/StabilityMatrix/app.{#}.log", ArchiveNumbering = ArchiveNumberingMode.Rolling, MaxArchiveFiles = 2 } @@ -709,8 +662,11 @@ public sealed class App : Application builder.ForLogger("Microsoft.Extensions.Http.*").WriteToNil(NLog.LogLevel.Warn); // Disable console trace logging by default + builder.ForLogger("StabilityMatrix.Avalonia.ViewModels.ConsoleViewModel").WriteToNil(NLog.LogLevel.Debug); + + // Disable LoadableViewModelBase trace logging by default builder - .ForLogger("StabilityMatrix.Avalonia.ViewModels.ConsoleViewModel") + .ForLogger("StabilityMatrix.Avalonia.ViewModels.Base.LoadableViewModelBase") .WriteToNil(NLog.LogLevel.Debug); builder.ForLogger().FilterMinLevel(NLog.LogLevel.Trace).WriteTo(debugTarget); @@ -727,18 +683,20 @@ public sealed class App : Application // Sentry if (SentrySdk.IsEnabled) { - LogManager.Configuration.AddSentry(o => - { - o.InitializeSdk = false; - o.Layout = "${message}"; - o.ShutdownTimeoutSeconds = 5; - o.IncludeEventDataOnBreadcrumbs = true; - o.BreadcrumbLayout = "${logger}: ${message}"; - // Debug and higher are stored as breadcrumbs (default is Info) - o.MinimumBreadcrumbLevel = NLog.LogLevel.Debug; - // Error and higher is sent as event (default is Error) - o.MinimumEventLevel = NLog.LogLevel.Error; - }); + LogManager + .Configuration + .AddSentry(o => + { + o.InitializeSdk = false; + o.Layout = "${message}"; + o.ShutdownTimeoutSeconds = 5; + o.IncludeEventDataOnBreadcrumbs = true; + o.BreadcrumbLayout = "${logger}: ${message}"; + // Debug and higher are stored as breadcrumbs (default is Info) + o.MinimumBreadcrumbLevel = NLog.LogLevel.Debug; + // Error and higher is sent as event (default is Error) + o.MinimumEventLevel = NLog.LogLevel.Error; + }); } LogManager.ReconfigExistingLoggers(); @@ -777,36 +735,34 @@ public sealed class App : Application results.Add(ms); } - Dispatcher.UIThread.InvokeAsync(async () => - { - var dest = await StorageProvider.SaveFilePickerAsync( - new FilePickerSaveOptions() - { - SuggestedFileName = "screenshot.png", - ShowOverwritePrompt = true - } - ); - - if (dest?.TryGetLocalPath() is { } localPath) + Dispatcher + .UIThread + .InvokeAsync(async () => { - var localFile = new FilePath(localPath); - foreach (var (i, stream) in results.Enumerate()) + var dest = await StorageProvider.SaveFilePickerAsync( + new FilePickerSaveOptions() { SuggestedFileName = "screenshot.png", ShowOverwritePrompt = true } + ); + + if (dest?.TryGetLocalPath() is { } localPath) { - var name = localFile.NameWithoutExtension; - if (results.Count > 1) + var localFile = new FilePath(localPath); + foreach (var (i, stream) in results.Enumerate()) { - name += $"_{i + 1}"; - } + var name = localFile.NameWithoutExtension; + if (results.Count > 1) + { + name += $"_{i + 1}"; + } - localFile = localFile.Directory!.JoinFile(name + ".png"); - localFile.Create(); + localFile = localFile.Directory!.JoinFile(name + ".png"); + localFile.Create(); - await using var fileStream = localFile.Info.OpenWrite(); - stream.Seek(0, SeekOrigin.Begin); - await stream.CopyToAsync(fileStream); + await using var fileStream = localFile.Info.OpenWrite(); + stream.Seek(0, SeekOrigin.Begin); + await stream.CopyToAsync(fileStream); + } } - } - }); + }); } [Conditional("DEBUG")] @@ -822,8 +778,7 @@ public sealed class App : Application { #if DEBUG setupBuilder.SetupExtensions( - extensionBuilder => - extensionBuilder.RegisterTarget("DataStoreLogger") + extensionBuilder => extensionBuilder.RegisterTarget("DataStoreLogger") ); #endif } From f18f61669f5f7238a2702f95a5fd95e493056cd4 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 14:17:09 -0500 Subject: [PATCH 065/166] Use Dictionary instead of ImmutableDictionary --- .../Helpers/ViewModelSerializer.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/StabilityMatrix.Avalonia/Helpers/ViewModelSerializer.cs b/StabilityMatrix.Avalonia/Helpers/ViewModelSerializer.cs index 5039d082..238c67d7 100644 --- a/StabilityMatrix.Avalonia/Helpers/ViewModelSerializer.cs +++ b/StabilityMatrix.Avalonia/Helpers/ViewModelSerializer.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Reflection; using System.Text.Json.Serialization; @@ -9,16 +8,12 @@ namespace StabilityMatrix.Avalonia.Helpers; public static class ViewModelSerializer { - public static IImmutableDictionary GetDerivedTypes(Type baseType) + public static Dictionary GetDerivedTypes(Type baseType) { - return GetJsonDerivedTypeAttributes(baseType) - .ToImmutableDictionary(x => x.typeDiscriminator, x => x.subType); + return GetJsonDerivedTypeAttributes(baseType).ToDictionary(x => x.typeDiscriminator, x => x.subType); } - public static IEnumerable<( - Type subType, - string typeDiscriminator - )> GetJsonDerivedTypeAttributes(Type type) + public static IEnumerable<(Type subType, string typeDiscriminator)> GetJsonDerivedTypeAttributes(Type type) { return type.GetCustomAttributes() .Select(x => (x.DerivedType, x.TypeDiscriminator as string ?? x.DerivedType.Name)); From b136590c3cb12722437d6b5a52216693fd7ee3e1 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 14:17:28 -0500 Subject: [PATCH 066/166] More module localization --- .../Languages/Resources.Designer.cs | 18 ++++++++ .../Languages/Resources.resx | 6 +++ .../Inference/Modules/SaveImageModule.cs | 21 +++++---- .../Inference/SamplerCardViewModel.cs | 43 ++++++------------- 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index c1a86510..84018926 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -599,6 +599,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Addons. + /// + public static string Label_Addons { + get { + return ResourceManager.GetString("Label_Addons", resourceCulture); + } + } + /// /// Looks up a localized string similar to Add Stability Matrix to the Start Menu. /// @@ -1760,6 +1769,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Save Intermediate Image. + /// + public static string Label_SaveIntermediateImage { + get { + return ResourceManager.GetString("Label_SaveIntermediateImage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Search.... /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index d7b1cca5..e3864dbb 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -858,4 +858,10 @@ System Information + + AddonsInference Sampler Addons + + + Save Intermediate ImageInference module step to save an intermediate image + diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/SaveImageModule.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/SaveImageModule.cs index 72e3bb22..cfe39389 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/SaveImageModule.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/SaveImageModule.cs @@ -1,4 +1,5 @@ -using StabilityMatrix.Avalonia.Models.Inference; +using StabilityMatrix.Avalonia.Languages; +using StabilityMatrix.Avalonia.Models.Inference; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Attributes; @@ -14,19 +15,21 @@ public class SaveImageModule : ModuleBase public SaveImageModule(ServiceManager vmFactory) : base(vmFactory) { - Title = "Save Intermediary Image"; + Title = Resources.Label_SaveIntermediateImage; } /// protected override void OnApplyStep(ModuleApplyStepEventArgs e) { - var preview = e.Builder.Nodes.AddTypedNode( - new ComfyNodeBuilder.PreviewImage - { - Name = e.Builder.Nodes.GetUniqueName("SaveIntermediaryImage"), - Images = e.Builder.GetPrimaryAsImage() - } - ); + var preview = e.Builder + .Nodes + .AddTypedNode( + new ComfyNodeBuilder.PreviewImage + { + Name = e.Builder.Nodes.GetUniqueName("SaveIntermediateImage"), + Images = e.Builder.GetPrimaryAsImage() + } + ); e.Builder.Connections.OutputNodes.Add(preview); } diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs index eb975d24..230b2f28 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs @@ -1,10 +1,11 @@ using System; -using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Runtime.Serialization; using System.Text.Json.Serialization; using CommunityToolkit.Mvvm.ComponentModel; using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.Models.Inference; using StabilityMatrix.Avalonia.Services; @@ -21,10 +22,7 @@ namespace StabilityMatrix.Avalonia.ViewModels.Inference; [View(typeof(SamplerCard))] [ManagedService] [Transient] -public partial class SamplerCardViewModel - : LoadableViewModelBase, - IParametersLoadableState, - IComfyStep +public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLoadableState, IComfyStep { public const string ModuleKey = "Sampler"; @@ -80,15 +78,12 @@ public partial class SamplerCardViewModel private int TotalSteps => Steps + RefinerSteps; - public SamplerCardViewModel( - IInferenceClientManager clientManager, - ServiceManager vmFactory - ) + public SamplerCardViewModel(IInferenceClientManager clientManager, ServiceManager vmFactory) { ClientManager = clientManager; ModulesCardViewModel = vmFactory.Get(modulesCard => { - modulesCard.Title = "Addons"; + modulesCard.Title = Resources.Label_Addons; modulesCard.AvailableModules = new[] { typeof(FreeUModule), typeof(ControlNetModule) }; modulesCard.InitializeDefaults(); }); @@ -146,8 +141,7 @@ public partial class SamplerCardViewModel var primaryLatent = e.Builder.GetPrimaryAsLatent(vae); // Set primary sampler and scheduler - e.Builder.Connections.PrimarySampler = - SelectedSampler ?? throw new ValidationException("Sampler not selected"); + e.Builder.Connections.PrimarySampler = SelectedSampler ?? throw new ValidationException("Sampler not selected"); e.Builder.Connections.PrimaryScheduler = SelectedScheduler ?? throw new ValidationException("Scheduler not selected"); @@ -159,9 +153,7 @@ public partial class SamplerCardViewModel new ComfyNodeBuilder.KSampler { Name = "Sampler", - Model = - e.Builder.Connections.BaseModel - ?? throw new ArgumentException("No BaseModel"), + Model = e.Builder.Connections.BaseModel ?? throw new ArgumentException("No BaseModel"), Seed = e.Builder.Connections.Seed, SamplerName = e.Builder.Connections.PrimarySampler?.Name!, Scheduler = e.Builder.Connections.PrimaryScheduler?.Name!, @@ -183,9 +175,7 @@ public partial class SamplerCardViewModel new ComfyNodeBuilder.KSamplerAdvanced { Name = "Sampler", - Model = - e.Builder.Connections.BaseModel - ?? throw new ArgumentException("No BaseModel"), + Model = e.Builder.Connections.BaseModel ?? throw new ArgumentException("No BaseModel"), AddNoise = true, NoiseSeed = e.Builder.Connections.Seed, Steps = TotalSteps, @@ -206,9 +196,7 @@ public partial class SamplerCardViewModel new ComfyNodeBuilder.KSamplerAdvanced { Name = "Refiner_Sampler", - Model = - e.Builder.Connections.RefinerModel - ?? throw new ArgumentException("No RefinerModel"), + Model = e.Builder.Connections.RefinerModel ?? throw new ArgumentException("No RefinerModel"), AddNoise = false, NoiseSeed = e.Builder.Connections.Seed, Steps = TotalSteps, @@ -254,18 +242,11 @@ public partial class SamplerCardViewModel if ( !string.IsNullOrEmpty(parameters.Sampler) - && GenerationParametersConverter.TryGetSamplerScheduler( - parameters.Sampler, - out var samplerScheduler - ) + && GenerationParametersConverter.TryGetSamplerScheduler(parameters.Sampler, out var samplerScheduler) ) { - SelectedSampler = ClientManager.Samplers.FirstOrDefault( - s => s == samplerScheduler.Sampler - ); - SelectedScheduler = ClientManager.Schedulers.FirstOrDefault( - s => s == samplerScheduler.Scheduler - ); + SelectedSampler = ClientManager.Samplers.FirstOrDefault(s => s == samplerScheduler.Sampler); + SelectedScheduler = ClientManager.Schedulers.FirstOrDefault(s => s == samplerScheduler.Scheduler); } } From 1f7faf964f242c862851b9b691af6cc13450d209 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 14:17:45 -0500 Subject: [PATCH 067/166] Remove redundant default set --- .../ViewModels/Inference/SamplerCardViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs index 230b2f28..77663967 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs @@ -85,7 +85,6 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo { modulesCard.Title = Resources.Label_Addons; modulesCard.AvailableModules = new[] { typeof(FreeUModule), typeof(ControlNetModule) }; - modulesCard.InitializeDefaults(); }); ModulesCardViewModel.CardAdded += ( From 0145e90e1f63911d21e728ed40737d5b5acce984 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 15:07:06 -0500 Subject: [PATCH 068/166] Cleanup commented styles --- .../Controls/StackEditableCard.axaml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/StackEditableCard.axaml b/StabilityMatrix.Avalonia/Controls/StackEditableCard.axaml index f89576d8..70d1a3b8 100644 --- a/StabilityMatrix.Avalonia/Controls/StackEditableCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/StackEditableCard.axaml @@ -62,19 +62,7 @@ - - - + From f2f20323ca03ecdb21d77de5c35b6270417bfd1e Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 3 Dec 2023 15:07:29 -0500 Subject: [PATCH 069/166] Use base type property --- .../ViewModels/Inference/StackEditableCardViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/StackEditableCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/StackEditableCardViewModel.cs index 7f2a1d35..d26b646c 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/StackEditableCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/StackEditableCardViewModel.cs @@ -62,7 +62,7 @@ public partial class StackEditableCardViewModel : StackViewModelBase partial void OnIsEditEnabledChanged(bool value) { // Propagate edit state to children - foreach (var module in Cards.OfType()) + foreach (var module in Cards.OfType()) { module.IsEditEnabled = value; } @@ -73,7 +73,7 @@ public partial class StackEditableCardViewModel : StackViewModelBase { base.OnCardAdded(item); - if (item is ModuleBase module) + if (item is StackExpanderViewModel module) { // Inherit our edit state module.IsEditEnabled = IsEditEnabled; From 570e40ca97bc47ea5b95c7aca5e02d8dc8a7ae7b Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 3 Dec 2023 14:40:58 -0800 Subject: [PATCH 070/166] Add links for invoke IP adapter stuff & finish hf-packages.json & fix drag/drop import --- .../Assets/hf-packages.json | 209 ++++++++++++++++++ .../HuggingFace/HuggingFaceModelType.cs | 12 +- .../Views/CheckpointsPage.axaml.cs | 10 +- .../Views/HuggingFacePage.axaml | 28 +-- .../Models/Packages/InvokeAI.cs | 110 +++------ .../Models/SharedFolderType.cs | 4 + 6 files changed, 278 insertions(+), 95 deletions(-) diff --git a/StabilityMatrix.Avalonia/Assets/hf-packages.json b/StabilityMatrix.Avalonia/Assets/hf-packages.json index 9b19edb3..e103d80e 100644 --- a/StabilityMatrix.Avalonia/Assets/hf-packages.json +++ b/StabilityMatrix.Avalonia/Assets/hf-packages.json @@ -331,5 +331,214 @@ ], "Subfolder": "shuffle", "LicenseType": "Open RAIL" + }, + { + "ModelCategory": "DiffusersClipVision", + "ModelName": "IP Adapter Encoder", + "RepositoryPath": "InvokeAI/ip_adapter_sd_image_encoder", + "Files": [ + "config.json", + "model.safetensors" + ], + "Subfolder": "ip_adapter_sd_image_encoder", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersClipVision", + "ModelName": "IP Adapter Encoder (SDXL)", + "RepositoryPath": "InvokeAI/ip_adapter_sdxl_image_encoder", + "Files": [ + "config.json", + "model.safetensors" + ], + "Subfolder": "ip_adapter_sd_image_encoder", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapter", + "ModelName": "SD 1.5 Adapter", + "RepositoryPath": "InvokeAI/ip_adapter_sd15", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_sd15", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapter", + "ModelName": "SD 1.5 Light Adapter", + "RepositoryPath": "InvokeAI/ip_adapter_sd15_light", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_sd15_light", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapter", + "ModelName": "SD 1.5 Plus Adapter", + "RepositoryPath": "InvokeAI/ip_adapter_plus_sd15", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_plus_sd15", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapter", + "ModelName": "SD 1.5 Face Plus Adapter", + "RepositoryPath": "InvokeAI/ip_adapter_plus_face_sd15", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_plus_face_sd15", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapterXl", + "ModelName": "SDXL Adapter", + "RepositoryPath": "InvokeAI/ip_adapter_sdxl", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_sdxl", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapterXl", + "ModelName": "SDXL Plus Adapter", + "RepositoryPath": "InvokeAI/ip-adapter-plus_sdxl_vit-h", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_plus_sdxl_vit-h", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersIpAdapterXl", + "ModelName": "SDXL Face Plus Adapter", + "RepositoryPath": "InvokeAI/ip-adapter-plus-face_sdxl_vit-h", + "Files": [ + "image_encoder.txt", + "ip_adapter.bin" + ], + "Subfolder": "ip_adapter_plus_face_sdxl_vit-h", + "LicenseType": "CreativeML Open RAIL-M" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Sketch", + "RepositoryPath": "TencentARC/t2iadapter_sketch_sd15v2", + "Files": [ + "config.json", + "diffusion_pytorch_model.bin" + ], + "Subfolder": "t2iadapter_sketch_sd15v2", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Depth", + "RepositoryPath": "TencentARC/t2iadapter_depth_sd15v2", + "Files": [ + "config.json", + "diffusion_pytorch_model.bin" + ], + "Subfolder": "t2iadapter_depth_sd15v2", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Canny", + "RepositoryPath": "TencentARC/t2iadapter_canny_sd15v2", + "Files": [ + "config.json", + "diffusion_pytorch_model.bin" + ], + "Subfolder": "t2iadapter_canny_sd15v2", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Depth-Zoe", + "RepositoryPath": "TencentARC/t2iadapter_zoedepth_sd15v1", + "Files": [ + "config.json", + "diffusion_pytorch_model.bin" + ], + "Subfolder": "t2iadapter_zoedepth_sd15v1", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Sketch (SDXL)", + "RepositoryPath": "TencentARC/t2i-adapter-sketch-sdxl-1.0", + "Files": [ + "config.json", + "diffusion_pytorch_model.safetensors" + ], + "Subfolder": "t2i-adapter-sketch-sdxl-1.0", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Depth-Zoe (SDXL)", + "RepositoryPath": "TencentARC/t2i-adapter-depth-zoe-sdxl-1.0", + "Files": [ + "config.json", + "diffusion_pytorch_model.safetensors" + ], + "Subfolder": "t2i-adapter-depth-zoe-sdxl-1.0", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "OpenPose (SDXL)", + "RepositoryPath": "TencentARC/t2i-adapter-openpose-sdxl-1.0", + "Files": [ + "config.json", + "diffusion_pytorch_model.safetensors" + ], + "Subfolder": "t2i-adapter-openpose-sdxl-1.0", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Depth-MiDaS (SDXL)", + "RepositoryPath": "TencentARC/t2i-adapter-depth-midas-sdxl-1.0", + "Files": [ + "config.json", + "diffusion_pytorch_model.safetensors" + ], + "Subfolder": "t2i-adapter-depth-midas-sdxl-1.0", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "LineArt (SDXL)", + "RepositoryPath": "TencentARC/t2i-adapter-lineart-sdxl-1.0", + "Files": [ + "config.json", + "diffusion_pytorch_model.safetensors" + ], + "Subfolder": "t2i-adapter-lineart-sdxl-1.0", + "LicenseType": "Apache 2.0" + }, + { + "ModelCategory": "DiffusersT2IAdapter", + "ModelName": "Canny (SDXL)", + "RepositoryPath": "TencentARC/t2i-adapter-canny-sdxl-1.0", + "Files": [ + "config.json", + "diffusion_pytorch_model.safetensors" + ], + "Subfolder": "t2i-adapter-canny-sdxl-1.0", + "LicenseType": "Apache 2.0" } ] diff --git a/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs b/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs index c2869e72..e9237b50 100644 --- a/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs +++ b/StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs @@ -24,10 +24,18 @@ public enum HuggingFaceModelType [ConvertTo(SharedFolderType.IpAdapter)] IpAdapter, - [Description("IP Adapters (Diffusers)")] - [ConvertTo(SharedFolderType.IpAdapter)] + [Description("IP Adapters (Diffusers SD1.5)")] + [ConvertTo(SharedFolderType.InvokeIpAdapters15)] DiffusersIpAdapter, + [Description("IP Adapters (Diffusers SDXL)")] + [ConvertTo(SharedFolderType.InvokeIpAdaptersXl)] + DiffusersIpAdapterXl, + + [Description("CLIP Vision (Diffusers)")] + [ConvertTo(SharedFolderType.InvokeClipVision)] + DiffusersClipVision, + [Description("T2I Adapters")] [ConvertTo(SharedFolderType.T2IAdapter)] T2IAdapter, diff --git a/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs b/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs index b7aec886..086780a3 100644 --- a/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml.cs @@ -69,7 +69,10 @@ public partial class CheckpointsPage : UserControlBase case CheckpointFolder folder: { if (e.Data.Get("Context") is not CheckpointFile file) - return; + { + await folder.OnDrop(e); + break; + } var filePath = new FilePath(file.FilePath); if (filePath.Directory?.FullPath != folder.DirectoryPath) @@ -81,7 +84,10 @@ public partial class CheckpointsPage : UserControlBase case CheckpointFile file: { if (e.Data.Get("Context") is not CheckpointFile dragFile) - return; + { + await file.ParentFolder.OnDrop(e); + break; + } var parentFolder = file.ParentFolder; var dragFilePath = new FilePath(dragFile.FilePath); diff --git a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml index 2e222757..4a1222b2 100644 --- a/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml +++ b/StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml @@ -169,20 +169,20 @@ - - - - - protected virtual void OnProgressUpdateReceived( - object? sender, - ComfyProgressUpdateEventArgs args - ) + protected virtual void OnProgressUpdateReceived(object? sender, ComfyProgressUpdateEventArgs args) { - Dispatcher.UIThread.Post(() => - { - OutputProgress.Value = args.Value; - OutputProgress.Maximum = args.Maximum; - OutputProgress.IsIndeterminate = false; + Dispatcher + .UIThread + .Post(() => + { + OutputProgress.Value = args.Value; + OutputProgress.Maximum = args.Maximum; + OutputProgress.IsIndeterminate = false; - OutputProgress.Text = - $"({args.Value} / {args.Maximum})" - + (args.RunningNode != null ? $" {args.RunningNode}" : ""); - }); + OutputProgress.Text = + $"({args.Value} / {args.Maximum})" + (args.RunningNode != null ? $" {args.RunningNode}" : ""); + }); } private void AttachRunningNodeChangedHandler(ComfyTask comfyTask) @@ -595,13 +544,15 @@ public abstract partial class InferenceGenerationViewModelBase return; } - Dispatcher.UIThread.Post(() => - { - OutputProgress.IsIndeterminate = true; - OutputProgress.Value = 100; - OutputProgress.Maximum = 100; - OutputProgress.Text = nodeName; - }); + Dispatcher + .UIThread + .Post(() => + { + OutputProgress.IsIndeterminate = true; + OutputProgress.Value = 100; + OutputProgress.Maximum = 100; + OutputProgress.Text = nodeName; + }); } public class ImageGenerationEventArgs : EventArgs @@ -629,11 +580,7 @@ public abstract partial class InferenceGenerationViewModelBase overrides[typeof(HiresFixModule)] = args.Overrides.IsHiresFixEnabled.Value; } - return new ModuleApplyStepEventArgs - { - Builder = args.Builder, - IsEnabledOverrides = overrides - }; + return new ModuleApplyStepEventArgs { Builder = args.Builder, IsEnabledOverrides = overrides }; } } } From e9707319da46e3cf1bb71fd24c4537fe3ea7442a Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 16:00:28 -0500 Subject: [PATCH 077/166] Use transparent style for disabled transparent-full --- StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml b/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml index ca95cb8d..b0af1750 100644 --- a/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml +++ b/StabilityMatrix.Avalonia/Styles/ButtonStyles.axaml @@ -377,8 +377,8 @@ - + + IsChecked="{Binding IsEnabled}" + OffContent="" + OnContent="" /> - + - + + - + - - + + - + - + - + - @@ -88,7 +100,7 @@ - + diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 8edb6ca8..2f9ca9c1 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Http; using System.Text; using AvaloniaEdit.Utils; +using CommunityToolkit.Mvvm.ComponentModel; using DynamicData; using DynamicData.Binding; using Microsoft.Extensions.DependencyInjection; @@ -631,7 +633,11 @@ The gallery images are often inpainted, but you will get something very similar public static PropertyGridViewModel PropertyGridViewModel => DialogFactory.Get(vm => { - vm.SelectedObject = new MockPropertyGridObject(); + vm.SelectedObject = new INotifyPropertyChanged[] + { + new MockPropertyGridObject(), + new MockPropertyGridObjectAlt() + }; vm.ExcludeCategories = ["Excluded Category"]; }); @@ -732,6 +738,15 @@ The gallery images are often inpainted, but you will get something very similar vm.OnContainerIndexChanged(0); }); + public static StackExpanderViewModel StackExpanderViewModel2 => + DialogFactory.Get(vm => + { + vm.Title = "Hires Fix"; + vm.IsSettingsEnabled = true; + vm.AddCards(UpscalerCardViewModel, SamplerCardViewModel); + vm.OnContainerIndexChanged(1); + }); + public static UpscalerCardViewModel UpscalerCardViewModel => DialogFactory.Get(); public static BatchSizeCardViewModel BatchSizeCardViewModel => DialogFactory.Get(); diff --git a/StabilityMatrix.Avalonia/DesignData/MockPropertyGridObject.cs b/StabilityMatrix.Avalonia/DesignData/MockPropertyGridObject.cs index 82969486..0facd0c2 100644 --- a/StabilityMatrix.Avalonia/DesignData/MockPropertyGridObject.cs +++ b/StabilityMatrix.Avalonia/DesignData/MockPropertyGridObject.cs @@ -42,3 +42,13 @@ public partial class MockPropertyGridObject : ObservableObject [property: Category("Excluded Category")] private string? stringExcludedCategoryProperty; } + +public partial class MockPropertyGridObjectAlt : ObservableObject +{ + [ObservableProperty] + private int altIntProperty = 10; + + [ObservableProperty] + [property: Category("Settings")] + private string? altStringProperty; +} diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs index 0a1eb21b..9a467227 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs @@ -1,8 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using Avalonia.PropertyGrid.ViewModels; using CommunityToolkit.Mvvm.ComponentModel; using OneOf; +using StabilityMatrix.Avalonia.DesignData; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; @@ -15,10 +18,11 @@ namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; public partial class PropertyGridViewModel : TaskDialogViewModelBase { [ObservableProperty] - [NotifyPropertyChangedFor(nameof(SelectedObjectSource))] + [NotifyPropertyChangedFor(nameof(SelectedObjectItemsSource))] private OneOf>? selectedObject; - public object? SelectedObjectSource => SelectedObject?.Value; + public IEnumerable? SelectedObjectItemsSource => + SelectedObject?.Match(single => [single], multiple => multiple); [ObservableProperty] private PropertyGridShowStyle showStyle = PropertyGridShowStyle.Alphabetic; diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml index 5a09bb05..e00459fc 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml @@ -1,22 +1,33 @@ - + - + + + + + + + + diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml.cs b/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml.cs index 77b4317e..7eb8526e 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml.cs +++ b/StabilityMatrix.Avalonia/Views/Dialogs/PropertyGridDialog.axaml.cs @@ -1,9 +1,4 @@ -using Avalonia.Controls.Primitives; -using Avalonia.PropertyGrid.Controls; -using Avalonia.PropertyGrid.ViewModels; -using PropertyModels.ComponentModel; -using StabilityMatrix.Avalonia.Controls; -using StabilityMatrix.Avalonia.ViewModels.Dialogs; +using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Core.Attributes; namespace StabilityMatrix.Avalonia.Views.Dialogs; @@ -15,40 +10,4 @@ public partial class PropertyGridDialog : UserControlBase { InitializeComponent(); } - - /// - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - - var vm = DataContext as PropertyGridViewModel; - - if (vm?.ExcludeCategories is { } excludeCategories) - { - MainPropertyGrid.FilterExcludeCategories(excludeCategories); - } - - if (vm?.IncludeCategories is { } includeCategories) - { - MainPropertyGrid.FilterIncludeCategories(includeCategories); - } - } - - internal class CustomFilter : IPropertyGridFilterContext - { - /// - public IFilterPattern? FilterPattern => null; - - /// - public ICheckedMaskModel? FastFilterPattern => null; - - /// - public PropertyVisibility PropagateVisibility( - IPropertyGridCellInfo info, - FilterCategory category = FilterCategory.Default - ) - { - return PropertyVisibility.AlwaysVisible; - } - } } From 1a999758da817635928364682ba83a94c3213036 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 17:19:34 -0500 Subject: [PATCH 079/166] Add settings option for stack expanders --- .../Dialogs/PropertyGridViewModel.cs | 5 +- .../Inference/Modules/ModuleBase.cs | 12 ++-- .../Inference/StackExpanderViewModel.cs | 12 +++- .../Extensions/EnumerableExtensions.cs | 72 +++++++++++-------- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs index 9a467227..d83f1db6 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using Avalonia.PropertyGrid.ViewModels; using CommunityToolkit.Mvvm.ComponentModel; using OneOf; -using StabilityMatrix.Avalonia.DesignData; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs index a8436b63..d8323406 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs @@ -8,18 +8,20 @@ namespace StabilityMatrix.Avalonia.ViewModels.Inference.Modules; public abstract class ModuleBase : StackExpanderViewModel, IComfyStep, IInputImageProvider { + protected readonly ServiceManager VmFactory; + /// protected ModuleBase(ServiceManager vmFactory) - : base(vmFactory) { } + : base(vmFactory) + { + VmFactory = vmFactory; + } /// public void ApplyStep(ModuleApplyStepEventArgs e) { if ( - ( - e.IsEnabledOverrides.TryGetValue(GetType(), out var isEnabledOverride) - && !isEnabledOverride - ) || !IsEnabled + (e.IsEnabledOverrides.TryGetValue(GetType(), out var isEnabledOverride) && !isEnabledOverride) || !IsEnabled ) { return; diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs index 4780cdd5..e3ed8966 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Text.Json.Nodes; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using Newtonsoft.Json; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Models.Inference; @@ -27,6 +28,9 @@ public partial class StackExpanderViewModel : StackViewModelBase [property: JsonIgnore] private string? titleExtra; + [ObservableProperty] + private bool isEnabled; + /// /// True if parent StackEditableCard is in edit mode (can drag to reorder) /// @@ -34,8 +38,12 @@ public partial class StackExpanderViewModel : StackViewModelBase [property: JsonIgnore] private bool isEditEnabled; - [ObservableProperty] - private bool isEnabled; + /// + /// True to show the settings button, invokes when clicked + /// + public virtual bool IsSettingsEnabled { get; set; } + + public virtual IRelayCommand? SettingsCommand { get; set; } /// public StackExpanderViewModel(ServiceManager vmFactory) diff --git a/StabilityMatrix.Core/Extensions/EnumerableExtensions.cs b/StabilityMatrix.Core/Extensions/EnumerableExtensions.cs index 26ae5452..49dfa962 100644 --- a/StabilityMatrix.Core/Extensions/EnumerableExtensions.cs +++ b/StabilityMatrix.Core/Extensions/EnumerableExtensions.cs @@ -2,47 +2,61 @@ public static class EnumerableExtensions { - public static IEnumerable<(int, T)> Enumerate( - this IEnumerable items, - int start - ) { + public static IEnumerable<(int, T)> Enumerate(this IEnumerable items, int start) + { return items.Select((item, index) => (index + start, item)); } - - public static IEnumerable<(int, T)> Enumerate( - this IEnumerable items - ) { + + public static IEnumerable<(int, T)> Enumerate(this IEnumerable items) + { return items.Select((item, index) => (index, item)); } - + /// /// Nested for loop helper /// public static IEnumerable<(T, T)> Product(this IEnumerable items, IEnumerable other) { - return from item1 in items - from item2 in other - select (item1, item2); - } - + return from item1 in items from item2 in other select (item1, item2); + } + public static async Task> SelectAsync( - this IEnumerable source, Func> method, - int concurrency = int.MaxValue) + this IEnumerable source, + Func> method, + int concurrency = int.MaxValue + ) { using var semaphore = new SemaphoreSlim(concurrency); - return await Task.WhenAll(source.Select(async s => + return await Task.WhenAll( + source.Select(async s => + { + try + { + // ReSharper disable once AccessToDisposedClosure + await semaphore.WaitAsync().ConfigureAwait(false); + return await method(s).ConfigureAwait(false); + } + finally + { + // ReSharper disable once AccessToDisposedClosure + semaphore.Release(); + } + }) + ) + .ConfigureAwait(false); + } + + /// + /// Executes a specified action on each element in a collection. + /// + /// The type of elements in the collection. + /// The collection to iterate over. + /// The action to perform on each element in the collection. + public static void ForEach(this IEnumerable items, Action action) + { + foreach (var item in items) { - try - { - // ReSharper disable once AccessToDisposedClosure - await semaphore.WaitAsync().ConfigureAwait(false); - return await method(s).ConfigureAwait(false); - } - finally - { - // ReSharper disable once AccessToDisposedClosure - semaphore.Release(); - } - })).ConfigureAwait(false); + action(item); + } } } From 0eba59f93b90526dda1785292085c8ff9662cec1 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 17:58:14 -0500 Subject: [PATCH 080/166] Add Toggle edit factory --- .../PropertyGrid/BetterPropertyGrid.cs | 3 + .../ToggleSwitchCellEditFactory.cs | 60 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 StabilityMatrix.Avalonia/Controls/PropertyGrid/ToggleSwitchCellEditFactory.cs diff --git a/StabilityMatrix.Avalonia/Controls/PropertyGrid/BetterPropertyGrid.cs b/StabilityMatrix.Avalonia/Controls/PropertyGrid/BetterPropertyGrid.cs index e9d22dc5..a03243fb 100644 --- a/StabilityMatrix.Avalonia/Controls/PropertyGrid/BetterPropertyGrid.cs +++ b/StabilityMatrix.Avalonia/Controls/PropertyGrid/BetterPropertyGrid.cs @@ -39,6 +39,9 @@ public class BetterPropertyGrid : global::Avalonia.PropertyGrid.Controls.Propert static BetterPropertyGrid() { + // Register factories + CellEditFactoryService.Default.AddFactory(new ToggleSwitchCellEditFactory()); + // Initialize localization and name resolver LocalizationService.Default.AddExtraService(new PropertyGridLocalizationService()); diff --git a/StabilityMatrix.Avalonia/Controls/PropertyGrid/ToggleSwitchCellEditFactory.cs b/StabilityMatrix.Avalonia/Controls/PropertyGrid/ToggleSwitchCellEditFactory.cs new file mode 100644 index 00000000..28232635 --- /dev/null +++ b/StabilityMatrix.Avalonia/Controls/PropertyGrid/ToggleSwitchCellEditFactory.cs @@ -0,0 +1,60 @@ +using Avalonia.Controls; +using Avalonia.PropertyGrid.Controls; +using Avalonia.PropertyGrid.Controls.Factories; +using Avalonia.PropertyGrid.Localization; + +namespace StabilityMatrix.Avalonia.Controls; + +internal class ToggleSwitchCellEditFactory : AbstractCellEditFactory +{ + // make this extend factor only effect on TestExtendPropertyGrid + public override bool Accept(object accessToken) + { + return accessToken is BetterPropertyGrid; + } + + public override Control? HandleNewProperty(PropertyCellContext context) + { + var propertyDescriptor = context.Property; + var target = context.Target; + + if (propertyDescriptor.PropertyType != typeof(bool)) + { + return null; + } + + var control = new ToggleSwitch(); + control.SetLocalizeBinding(ToggleSwitch.OnContentProperty, "On"); + control.SetLocalizeBinding(ToggleSwitch.OffContentProperty, "Off"); + + control.IsCheckedChanged += (s, e) => + { + SetAndRaise(context, control, control.IsChecked); + }; + + return control; + } + + public override bool HandlePropertyChanged(PropertyCellContext context) + { + var propertyDescriptor = context.Property; + var target = context.Target; + var control = context.CellEdit; + + if (propertyDescriptor.PropertyType != typeof(bool)) + { + return false; + } + + ValidateProperty(control, propertyDescriptor, target); + + if (control is ToggleSwitch ts) + { + ts.IsChecked = (bool)(propertyDescriptor.GetValue(target) ?? false); + + return true; + } + + return false; + } +} From f13a540cdad3399a23d4ceff777c3437c3dec4e6 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 17:58:40 -0500 Subject: [PATCH 081/166] Fix settings and delete button visuals --- .../Controls/StackExpander.axaml | 22 +++++++++---------- .../DesignData/DesignData.cs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml index 806d2fc2..4de3a2a1 100644 --- a/StabilityMatrix.Avalonia/Controls/StackExpander.axaml +++ b/StabilityMatrix.Avalonia/Controls/StackExpander.axaml @@ -37,7 +37,7 @@ - + - - + + diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 2f9ca9c1..ee142d7b 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -727,7 +727,7 @@ The gallery images are often inpainted, but you will get something very similar public static StackEditableCardViewModel StackEditableCardViewModel => DialogFactory.Get(vm => { - vm.AddCards(StackExpanderViewModel, StackExpanderViewModel); + vm.AddCards(StackExpanderViewModel, StackExpanderViewModel2); }); public static StackExpanderViewModel StackExpanderViewModel => From 9c563bacf292be691175bee3b07c57ba34a59a64 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 17:59:42 -0500 Subject: [PATCH 082/166] Add Settings localized string --- StabilityMatrix.Avalonia/Languages/Resources.Designer.cs | 9 +++++++++ StabilityMatrix.Avalonia/Languages/Resources.resx | 3 +++ 2 files changed, 12 insertions(+) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index 84018926..36526090 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -1805,6 +1805,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Settings. + /// + public static string Label_Settings { + get { + return ResourceManager.GetString("Label_Settings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shared Model Folder Strategy. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index e3864dbb..79ce5623 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -864,4 +864,7 @@ Save Intermediate ImageInference module step to save an intermediate image + + Settings + From 3f79a6e71908904a183f641d45447e0b6a2d63fa Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 17:59:58 -0500 Subject: [PATCH 083/166] Add Title properties for dialog viewmodel bases --- .../Base/ContentDialogViewModelBase.cs | 22 ++++++++++++++++--- .../Base/TaskDialogViewModelBase.cs | 10 ++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs index c82b6721..457267ad 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogViewModelBase.cs @@ -1,27 +1,43 @@ using System; +using Avalonia.Threading; using FluentAvalonia.UI.Controls; +using StabilityMatrix.Avalonia.Controls; namespace StabilityMatrix.Avalonia.ViewModels.Base; public class ContentDialogViewModelBase : ViewModelBase { + public virtual string? Title { get; set; } + // Events for button clicks public event EventHandler? PrimaryButtonClick; public event EventHandler? SecondaryButtonClick; public event EventHandler? CloseButtonClick; - + public virtual void OnPrimaryButtonClick() { PrimaryButtonClick?.Invoke(this, ContentDialogResult.Primary); } - + public virtual void OnSecondaryButtonClick() { SecondaryButtonClick?.Invoke(this, ContentDialogResult.Secondary); } - + public virtual void OnCloseButtonClick() { CloseButtonClick?.Invoke(this, ContentDialogResult.None); } + + /// + /// Return a that uses this view model as its content + /// + public virtual BetterContentDialog GetDialog() + { + Dispatcher.UIThread.VerifyAccess(); + + var dialog = new BetterContentDialog { Title = Title, Content = this }; + + return dialog; + } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/TaskDialogViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/TaskDialogViewModelBase.cs index 6825c12e..a09db95d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/TaskDialogViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/TaskDialogViewModelBase.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using System.Windows.Input; using Avalonia.Threading; +using AvaloniaEdit.Rendering; using FluentAvalonia.UI.Controls; using StabilityMatrix.Avalonia.Languages; @@ -13,6 +14,8 @@ public abstract class TaskDialogViewModelBase : ViewModelBase { private TaskDialog? dialog; + public virtual string? Title { get; set; } + protected static TaskDialogCommand GetCommandButton(string text, ICommand command) { return new TaskDialogCommand @@ -27,11 +30,7 @@ public abstract class TaskDialogViewModelBase : ViewModelBase protected static TaskDialogButton GetCloseButton() { - return new TaskDialogButton - { - Text = Resources.Action_Close, - DialogResult = TaskDialogStandardResult.Close - }; + return new TaskDialogButton { Text = Resources.Action_Close, DialogResult = TaskDialogStandardResult.Close }; } /// @@ -43,6 +42,7 @@ public abstract class TaskDialogViewModelBase : ViewModelBase dialog = new TaskDialog { + Header = Title, Content = this, XamlRoot = App.VisualRoot, Buttons = { GetCloseButton() } From 1692cf5cb9194cd37768b44ce44112b718ccc544 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 18:00:22 -0500 Subject: [PATCH 084/166] Add settings editor for HiresFixModule --- .../Dialogs/PropertyGridViewModel.cs | 17 ++++- .../Inference/Modules/HiresFixModule.cs | 76 +++++++++++++------ .../Inference/SamplerCardViewModel.cs | 5 ++ 3 files changed, 73 insertions(+), 25 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs index d83f1db6..757aee15 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PropertyGridViewModel.cs @@ -1,8 +1,11 @@ using System.Collections.Generic; using System.ComponentModel; +using Avalonia; using Avalonia.PropertyGrid.ViewModels; using CommunityToolkit.Mvvm.ComponentModel; using OneOf; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; @@ -12,7 +15,7 @@ namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; [View(typeof(PropertyGridDialog))] [ManagedService] [Transient] -public partial class PropertyGridViewModel : TaskDialogViewModelBase +public partial class PropertyGridViewModel : ContentDialogViewModelBase { [ObservableProperty] [NotifyPropertyChangedFor(nameof(SelectedObjectItemsSource))] @@ -29,4 +32,16 @@ public partial class PropertyGridViewModel : TaskDialogViewModelBase [ObservableProperty] private IReadOnlyList? includeCategories; + + /// + public override BetterContentDialog GetDialog() + { + var dialog = base.GetDialog(); + + dialog.Padding = new Thickness(0); + dialog.CloseOnClickOutside = true; + dialog.CloseButtonText = Resources.Action_Close; + + return dialog; + } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/HiresFixModule.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/HiresFixModule.cs index 3d3d210c..b09a115f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/HiresFixModule.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/HiresFixModule.cs @@ -1,8 +1,15 @@ using System; -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Input; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Models.Inference; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Extensions; using StabilityMatrix.Core.Models.Api.Comfy; @@ -12,8 +19,14 @@ namespace StabilityMatrix.Avalonia.ViewModels.Inference.Modules; [ManagedService] [Transient] -public class HiresFixModule : ModuleBase +public partial class HiresFixModule : ModuleBase { + /// + public override bool IsSettingsEnabled => true; + + /// + public override IRelayCommand SettingsCommand => OpenSettingsDialogCommand; + /// public HiresFixModule(ServiceManager vmFactory) : base(vmFactory) @@ -28,6 +41,19 @@ public class HiresFixModule : ModuleBase ); } + [RelayCommand] + private async Task OpenSettingsDialog() + { + var gridVm = VmFactory.Get(vm => + { + vm.Title = $"{Title} {Resources.Label_Settings}"; + vm.SelectedObject = Cards.ToArray(); + vm.IncludeCategories = ["Settings"]; + }); + + await gridVm.GetDialog().ShowAsync(); + } + /// protected override void OnApplyStep(ModuleApplyStepEventArgs e) { @@ -55,28 +81,30 @@ public class HiresFixModule : ModuleBase ); } - var hiresSampler = builder.Nodes.AddTypedNode( - new ComfyNodeBuilder.KSampler - { - Name = builder.Nodes.GetUniqueName("HiresFix_Sampler"), - Model = builder.Connections.GetRefinerOrBaseModel(), - Seed = builder.Connections.Seed, - Steps = samplerCard.Steps, - Cfg = samplerCard.CfgScale, - SamplerName = - samplerCard.SelectedSampler?.Name - ?? e.Builder.Connections.PrimarySampler?.Name - ?? throw new ArgumentException("No PrimarySampler"), - Scheduler = - samplerCard.SelectedScheduler?.Name - ?? e.Builder.Connections.PrimaryScheduler?.Name - ?? throw new ArgumentException("No PrimaryScheduler"), - Positive = builder.Connections.GetRefinerOrBaseConditioning(), - Negative = builder.Connections.GetRefinerOrBaseNegativeConditioning(), - LatentImage = builder.GetPrimaryAsLatent(), - Denoise = samplerCard.DenoiseStrength - } - ); + var hiresSampler = builder + .Nodes + .AddTypedNode( + new ComfyNodeBuilder.KSampler + { + Name = builder.Nodes.GetUniqueName("HiresFix_Sampler"), + Model = builder.Connections.GetRefinerOrBaseModel(), + Seed = builder.Connections.Seed, + Steps = samplerCard.Steps, + Cfg = samplerCard.CfgScale, + SamplerName = + samplerCard.SelectedSampler?.Name + ?? e.Builder.Connections.PrimarySampler?.Name + ?? throw new ArgumentException("No PrimarySampler"), + Scheduler = + samplerCard.SelectedScheduler?.Name + ?? e.Builder.Connections.PrimaryScheduler?.Name + ?? throw new ArgumentException("No PrimaryScheduler"), + Positive = builder.Connections.GetRefinerOrBaseConditioning(), + Negative = builder.Connections.GetRefinerOrBaseNegativeConditioning(), + LatentImage = builder.GetPrimaryAsLatent(), + Denoise = samplerCard.DenoiseStrength + } + ); // Set as primary builder.Connections.Primary = hiresSampler.Output; diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs index 77663967..54add49d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; @@ -16,6 +17,7 @@ using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Api.Comfy; using StabilityMatrix.Core.Models.Api.Comfy.Nodes; +#pragma warning disable CS0657 // Not a valid attribute location for this declaration namespace StabilityMatrix.Avalonia.ViewModels.Inference; @@ -42,6 +44,7 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo private double denoiseStrength = 1; [ObservableProperty] + [property: Category("Settings")] private bool isCfgScaleEnabled; [ObservableProperty] @@ -57,6 +60,7 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo private int height = 512; [ObservableProperty] + [property: Category("Settings")] private bool isSamplerSelectionEnabled; [ObservableProperty] @@ -64,6 +68,7 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo private ComfySampler? selectedSampler = ComfySampler.EulerAncestral; [ObservableProperty] + [property: Category("Settings")] private bool isSchedulerSelectionEnabled; [ObservableProperty] From f761927e03d10819a9149b6f42faa31b8c218209 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 21:43:22 -0500 Subject: [PATCH 085/166] Fix hires overrides --- .../ViewModels/Inference/Modules/ModuleBase.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs index d8323406..3b38b0f6 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/Modules/ModuleBase.cs @@ -20,9 +20,17 @@ public abstract class ModuleBase : StackExpanderViewModel, IComfyStep, IInputIma /// public void ApplyStep(ModuleApplyStepEventArgs e) { - if ( - (e.IsEnabledOverrides.TryGetValue(GetType(), out var isEnabledOverride) && !isEnabledOverride) || !IsEnabled - ) + if (e.IsEnabledOverrides.TryGetValue(GetType(), out var isEnabledOverride)) + { + if (isEnabledOverride) + { + OnApplyStep(e); + } + + return; + } + + if (!IsEnabled) { return; } From 3d08b0f8c1d5ecaf25d94ce4c7708da4d2fc5df7 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 4 Dec 2023 21:54:50 -0500 Subject: [PATCH 086/166] Add changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5401f44b..0404d9b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to Stability Matrix will be documented in this file. 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.7.0-pre.4 +### Added +#### Inference +- Added Image to Image project type +- Added Modular custom steps + - Use the plus button to add new steps (Hires Fix, Upscaler, and Save Image are currently available), and the edit button to enable removing or dragging steps to reorder them. This enables multi-pass Hires Fix, mixing different upscalers, and saving intermediate images at any point in the pipeline. +- Added Sampler addons + - Addons usually affect guidance like ControlNet, T2I, FreeU, and other addons to come. They apply to the individual sampler, so you can mix and match different ControlNets for Base and Hires Fix, or use the current output from a previous sampler as ControlNet guidance image for HighRes passes. +### Fixed +- Fixed Refiner model enabled state not saving to Inference project files + ## v2.7.0-pre.3 ### Added - Added "Find Connected Metadata" options for root-level and file-level scans to the Checkpoints page From b5a29aee8155600d5f3e0235cc7c45dbd4d63bed Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 5 Dec 2023 14:34:36 -0500 Subject: [PATCH 087/166] Add editorconfig for ordefault exclusion for CA1826 --- .editorconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index ddc54b53..15cae5b1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,7 @@ dotnet_sort_system_directives_first = true # ReSharper properties resharper_csharp_max_line_length = 120 + +# dotnet code quality +# noinspection EditorConfigKeyCorrectness +dotnet_code_quality.CA1826.exclude_ordefault_methods = true From bc578baac54d7a0cdcc889359e3c02ffa37c4cf6 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 5 Dec 2023 15:17:45 -0500 Subject: [PATCH 088/166] Simply image selection and fix CurrentBitmapSize null propagation --- .../Extensions/DataObjectExtensions.cs | 19 +++ .../Inference/SelectImageCardViewModel.cs | 128 +++++++----------- 2 files changed, 70 insertions(+), 77 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Extensions/DataObjectExtensions.cs diff --git a/StabilityMatrix.Avalonia/Extensions/DataObjectExtensions.cs b/StabilityMatrix.Avalonia/Extensions/DataObjectExtensions.cs new file mode 100644 index 00000000..ef6a47f0 --- /dev/null +++ b/StabilityMatrix.Avalonia/Extensions/DataObjectExtensions.cs @@ -0,0 +1,19 @@ +using Avalonia.Input; + +namespace StabilityMatrix.Avalonia.Extensions; + +public static class DataObjectExtensions +{ + /// + /// Get Context from IDataObject, set by Xaml Behaviors + /// + public static T? GetContext(this IDataObject dataObject) + { + if (dataObject.Get("Context") is T context) + { + return context; + } + + return default; + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/SelectImageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/SelectImageCardViewModel.cs index 688a062e..2ac24afe 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/SelectImageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/SelectImageCardViewModel.cs @@ -19,10 +19,7 @@ using StabilityMatrix.Avalonia.Models.Inference; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Attributes; -using StabilityMatrix.Core.Extensions; -using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models.Database; -using StabilityMatrix.Core.Models.FileInterfaces; using Size = System.Drawing.Size; namespace StabilityMatrix.Avalonia.ViewModels.Inference; @@ -52,10 +49,9 @@ public partial class SelectImageCardViewModel(INotificationService notificationS public bool IsSelectionAvailable => IsSelectionEnabled && ImageSource == null; public Size? CurrentBitmapSize => - new Size( - Convert.ToInt32(CurrentBitmap?.Size.Width ?? 0), - Convert.ToInt32(CurrentBitmap?.Size.Height ?? 0) - ); + CurrentBitmap is null + ? null + : new Size(Convert.ToInt32(CurrentBitmap.Size.Width), Convert.ToInt32(CurrentBitmap.Size.Height)); /// public void ApplyStep(ModuleApplyStepEventArgs e) @@ -67,57 +63,25 @@ public partial class SelectImageCardViewModel(INotificationService notificationS ); } - [MethodImpl(MethodImplOptions.Synchronized)] - private void LoadUserImage(ImageSource image) - { - var current = ImageSource; - - ImageSource = image; - - current?.Dispose(); - - // Cache the hash for later upload use - image.GetBlake3HashAsync().SafeFireAndForget(); - } - [RelayCommand] private async Task SelectImageFromFilePickerAsync() { var files = await App.StorageProvider.OpenFilePickerAsync( - new FilePickerOpenOptions - { - FileTypeFilter = new List - { - new("Png") { Patterns = new[] { "*.png" } }, - new("Jpg") { Patterns = new[] { "*.jpg", "*.jpeg" } } - } - } + new FilePickerOpenOptions { FileTypeFilter = [FilePickerFileTypes.ImagePng, FilePickerFileTypes.ImageJpg] } ); - if (files.Count == 0) - return; - - var image = new ImageSource(files[0].TryGetLocalPath()!); - - LoadUserImage(image); + if (files.FirstOrDefault()?.TryGetLocalPath() is { } path) + { + LoadUserImageSafe(new ImageSource(path)); + } } - /// + /// + /// Supports LocalImageFile Context or OS Files + /// public void DragOver(object? sender, DragEventArgs e) { - // 1. Context drop for LocalImageFile - if (e.Data.GetDataFormats().Contains("Context")) - { - if (e.Data.Get("Context") is LocalImageFile imageFile) - { - e.Handled = true; - return; - } - - e.DragEffects = DragDropEffects.None; - } - // 2. OS Files - if (e.Data.GetDataFormats().Contains(DataFormats.Files)) + if (e.Data.GetDataFormats().Contains(DataFormats.Files) || e.Data.GetContext() is not null) { e.Handled = true; return; @@ -130,43 +94,53 @@ public partial class SelectImageCardViewModel(INotificationService notificationS public void Drop(object? sender, DragEventArgs e) { // 1. Context drop for LocalImageFile - if (e.Data.GetDataFormats().Contains("Context")) + if (e.Data.GetContext() is { } imageFile) { - if (e.Data.Get("Context") is LocalImageFile imageFile) - { - e.Handled = true; + e.Handled = true; - Dispatcher.UIThread.Post( - () => LoadUserImage(new ImageSource(imageFile.AbsolutePath)) - ); + Dispatcher.UIThread.Post(() => LoadUserImageSafe(new ImageSource(imageFile.AbsolutePath))); - return; - } + return; } // 2. OS Files - if (e.Data.GetDataFormats().Contains(DataFormats.Files)) + if (e.Data.GetFiles() is { } files && files.Select(f => f.TryGetLocalPath()).FirstOrDefault() is { } path) { e.Handled = true; - try - { - if (e.Data.Get(DataFormats.Files) is IEnumerable files) - { - var path = files.Select(f => f.Path.LocalPath).FirstOrDefault(); - - if (string.IsNullOrWhiteSpace(path)) - { - return; - } - - Dispatcher.UIThread.Post(() => LoadUserImage(new ImageSource(path))); - } - } - catch (Exception ex) - { - Logger.Warn(ex, "Failed to load image from drop"); - notificationService.ShowPersistent("Failed to load source image", ex.Message); - } + Dispatcher.UIThread.Post(() => LoadUserImageSafe(new ImageSource(path))); + } + } + + /// + /// Calls with notification error handling. + /// + private void LoadUserImageSafe(ImageSource image) + { + try + { + LoadUserImage(image); + } + catch (Exception e) + { + Logger.Warn(e, "Error loading image"); + notificationService.ShowPersistent("Error loading image", e.Message); } } + + /// + /// Loads the user image from the given ImageSource. + /// + /// The ImageSource object representing the user image. + [MethodImpl(MethodImplOptions.Synchronized)] + private void LoadUserImage(ImageSource image) + { + var current = ImageSource; + + ImageSource = image; + + current?.Dispose(); + + // Cache the hash for later upload use + image.GetBlake3HashAsync().SafeFireAndForget(); + } } From 3d79113ef4da1b7f4109856caabf9afed4df8348 Mon Sep 17 00:00:00 2001 From: Ionite Date: Tue, 5 Dec 2023 15:52:50 -0500 Subject: [PATCH 089/166] Add replace image option for SelectImageCard --- .../Controls/SelectImageCard.axaml | 140 +++++++++++------- .../Languages/Resources.Designer.cs | 18 +++ .../Languages/Resources.resx | 6 + 3 files changed, 112 insertions(+), 52 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/SelectImageCard.axaml b/StabilityMatrix.Avalonia/Controls/SelectImageCard.axaml index b6dd0a2b..3b64a12d 100644 --- a/StabilityMatrix.Avalonia/Controls/SelectImageCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/SelectImageCard.axaml @@ -1,102 +1,138 @@ - - + + - - + + - +