Skip to content

Commit be75e6d

Browse files
authored
Merge branch 'main' into docs-site-scaffold
2 parents bca656d + eff0148 commit be75e6d

83 files changed

Lines changed: 3854 additions & 1411 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,39 @@ All notable changes to Stability Matrix will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
77

8+
## v2.16.1
9+
### Added
10+
- Added **automatic text encoder and VAE selection** to the Inference Model card. Selecting a model now fills any empty encoder slots and the default VAE with the matching local files for the detected workflow, so you don't need to know which files pair with which architecture (e.g. `qwen_3_4b` or `qwen_3_8b` + Flux.2 VAE for Flux.2 Klein, `clip_l` + `t5xxl` for Flux, `qwen_3_06b` + `qwen_image_vae` for Anima). Anything you pick manually is never overridden
11+
- Added a **misplaced-model warning** to the Inference Model card with a one-click **Move** button. If a model sits in a folder that can't work with the selected workflow (like a Z-Image or Anima file in the StableDiffusion folder), a compact warning explains the problem instead of letting generation fail with a cryptic ComfyUI error. The Move button relocates the file and its metadata to the right folder, then re-selects it. Dismissible per model
12+
- Added a **native macOS menu bar** with the standard application menu — About Stability Matrix, **Settings… (⌘,)**, and the usual Services / Hide / **Quit (⌘Q)** items
13+
### Changed
14+
- The Inference **Workflow** selector now switches the model loader to match the chosen profile, showing or hiding the separate encoder and VAE fields as appropriate. It will never switch to a loader that can't load the selected file; you get the warning above instead
15+
- Renamed the "Anima / SD" workflow profile to **"Anima"**. Anima has no all-in-one version, so it's now handled like Z-Image: standalone model in DiffusionModels with a separate text encoder and VAE
16+
- Image Lab's Flux.2 Klein model checks now match the text encoder to your selected UNET variant (4B vs 9B), and switching variants updates the status banner immediately
17+
- Image Lab's Flux Kontext and Qwen Image Edit providers now show ComfyUI's actual workflow rejection message instead of a generic "Generation failed" (Flux.2 Klein already did this)
18+
- Image Lab now opens the same ComfyUI error detail dialog that Inference uses when a workflow is rejected or a node fails mid-generation, showing the full error JSON instead of a truncated toast
19+
- Keyboard shortcuts now use **⌘ (Command)** instead of Ctrl on macOS — save/open, Generate, undo/redo, copy/paste/cut, tab navigation, and the mask editor all follow the platform convention, and context menus show the ⌘ glyph
20+
### Fixed
21+
- Fixed [#1659](https://github.com/LykosAI/StabilityMatrix/issues/1659) - Z-Image and Anima workflows hiding the Text Encoder selectors and passing an invalid `None` CLIP input to ComfyUI; standalone workflows now expose and automatically fill compatible text encoders and VAEs
22+
- Fixed [#1654](https://github.com/LykosAI/StabilityMatrix/issues/1654) and [#1658](https://github.com/LykosAI/StabilityMatrix/issues/1658) - Inference Image-to-Image masks could disappear in Linux AppImage builds, fail to appear in the image-card preview, or crash the app when saving an enabled clipping mask because Avalonia retained pixels owned by a disposed Skia image; converted mask bitmaps now own their pixel data and previews refresh after editing, restoring a project, or loading image dimensions
23+
- Fixed [#1660](https://github.com/LykosAI/StabilityMatrix/issues/1660) - CivArchive downloads always saving to the model folder root; the primary Download button now offers the inferred folder, existing subdirectories, and a custom folder picker, while filename patterns containing path separators create missing nested subfolders and keep downloads inside the selected models directory
24+
- Fixed [#1661](https://github.com/LykosAI/StabilityMatrix/issues/1661) - Custom UNet workflows missing current ComfyUI encoder types such as `sdxl`; the selector now includes current CLIP loader values while retaining single-encoder compatibility and migrating legacy `HiDream` casing
25+
- Fixed [#1664](https://github.com/LykosAI/StabilityMatrix/issues/1664) - Gemini failures with a saved key being misreported as "API key not configured"; Image Lab now shows the actual invalid-key, billing, quota, or permission error and explains that Nano Banana image generation requires a paid-tier API key from a Google AI project with billing enabled
26+
- Fixed **"No text encoders configured"** errors when generating with an all-in-one checkpoint after a UNet model had been selected in the same tab
27+
- Fixed Qwen Image Edit in Image Lab failing mid-generation when a wrong-size Qwen2.5-VL text encoder was installed. The **7B** encoder is now required, and the correct download is offered when it's missing
28+
- Fixed Image Lab reporting "all models present" for Flux.2 Klein 9B setups that only had the 4B text encoder (and vice versa). The matching encoder download is now offered
29+
- Fixed Image Lab model and LoRA dropdowns hiding files whose CivitAI base model tag is unrecognized (commonly "Other"), even when the filename clearly matches the provider
30+
- Fixed Animagine XL and other SDXL models with "anima" in the name being misdetected as the Anima architecture
31+
- Fixed the running-package exit confirmation not appearing when quitting via **⌘Q, the macOS app menu, or the Dock** — it previously only showed when closing the window directly, so those paths could tear down running packages without warning. All quit paths now prompt
32+
- Fixed an empty strip of space appearing below the native window title bar on macOS and Linux; the Windows-only caption area (icon, title, and min/max/close buttons) is now collapsed on those platforms so content sits directly under the system title bar
33+
### Performance
34+
- Optimized SKBitmap-to-WriteableBitmap conversion used throughout Inference and Image Lab; pixel data is now copied directly native-to-native (single bulk copy when strides match, per-row otherwise) instead of round-tripping every scanline through a temporary managed buffer, reducing allocations and GC pressure on full-resolution images
35+
### Supporters
36+
#### 🌟 Visionaries
37+
A massive thank you to our brilliant Visionaries: **Waterclouds**, **bluepopsicle**, **Ibixat**, **Droolguy**, **snotty**, **LG**, **whudunit**, **MrMxyzptlk12836**, **Psilocyfer18731**, **KalAbaddon**, and **moon_milky2843**! There's a little of your support behind every fix and refinement in this update, and we're grateful for all of it. A warm welcome to our newest Visionary, **cusalapapen1481**; it's wonderful to have you with us! 💛
38+
#### 🚀 Pioneers
39+
And an equally big thank you to our fantastic Pioneer crew, all familiar faces this time around: **Szir777**, **[USA]TechDude**, **SinthCore**, **Jisuren**, **Tigon**, **jweg79**, **rwx14662**, **Hurbie53**, **ahnhj.al**, **drew.lukas**, **Tuskaruho**, **Cjloha**, **Alligator1907**, **Bitti**, **Ghislain G**, **CommissarGiygas16050**, **qob97515211**, **bastardofbethlehem**, and **Zombop**! You keep showing up for us, and that steadiness is a big part of how this project keeps moving forward. Thank you, truly, every one of you. 💛
40+
841
## v2.16.0
942
### Added
1043
#### New Feature: 🧪 Image Lab - Conversational Image Generation for ComfyUI

StabilityMatrix.Avalonia/App.axaml.cs

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Net;
44
using System.Net.Http.Headers;
55
using System.Reflection;
6+
using System.Runtime.Versioning;
67
using System.Text.Json;
78
using System.Text.Json.Serialization;
89
using Apizr;
@@ -12,6 +13,7 @@
1213
using Avalonia.Controls;
1314
using Avalonia.Controls.ApplicationLifetimes;
1415
using Avalonia.Data.Core.Plugins;
16+
using Avalonia.Input;
1517
using Avalonia.Input.Platform;
1618
using Avalonia.Markup.Xaml;
1719
using Avalonia.Media;
@@ -100,6 +102,18 @@ public sealed class App : Application
100102

101103
private bool isOnExitComplete;
102104

105+
/// <summary>
106+
/// True once the user has confirmed exiting while packages are running, so
107+
/// <see cref="OnShutdownRequested"/> doesn't prompt again on the follow-up shutdown.
108+
/// </summary>
109+
private bool isExitConfirmed;
110+
111+
/// <summary>
112+
/// True while the exit confirmation dialog is open, to avoid stacking dialogs if more
113+
/// shutdown requests arrive (e.g. ⌘Q pressed repeatedly).
114+
/// </summary>
115+
private bool isConfirmingExit;
116+
103117
private ServiceProvider? serviceProvider;
104118

105119
[NotNull]
@@ -140,6 +154,13 @@ public override void Initialize()
140154

141155
SetFontFamily(GetPlatformDefaultFontFamily());
142156

157+
// macOS app menu must be set here (before AppBuilder's AfterSetup creates the menu
158+
// exporter) so it becomes the app menu and Avalonia appends the standard Services/Hide/Quit
159+
if (Compat.IsMacOS && !Design.IsDesignMode)
160+
{
161+
SetupMacOsApplicationMenu();
162+
}
163+
143164
// Set design theme
144165
if (Design.IsDesignMode)
145166
{
@@ -220,6 +241,47 @@ public override void OnFrameworkInitializationCompleted()
220241
}
221242
}
222243

244+
/// <summary>
245+
/// Sets the native macOS application menu. Avalonia's native backend reads the app menu (the
246+
/// bold app-name menu) from <see cref="NativeMenu"/> attached to the <see cref="Application"/>
247+
/// during AppBuilder's AfterSetup phase, then appends the standard Services / Hide / Quit (⌘Q)
248+
/// items. We add About and Settings… (⌘,) above those. Called from <see cref="Initialize"/>,
249+
/// which runs just before that phase, so it must not be moved any later or Avalonia falls back
250+
/// to its default "About Avalonia" menu.
251+
/// </summary>
252+
[SupportedOSPlatform("macos")]
253+
private void SetupMacOsApplicationMenu()
254+
{
255+
var aboutItem = new NativeMenuItem("About Stability Matrix");
256+
aboutItem.Click += (_, _) => ShowAboutDialog();
257+
258+
var settingsItem = new NativeMenuItem("Settings…")
259+
{
260+
Gesture = new KeyGesture(Key.OemComma, KeyModifiers.Meta),
261+
};
262+
settingsItem.Click += (_, _) =>
263+
Services
264+
.GetRequiredService<INavigationService<MainWindowViewModel>>()
265+
.NavigateTo<SettingsViewModel>();
266+
267+
var appMenu = new NativeMenu { Items = { aboutItem, new NativeMenuItemSeparator(), settingsItem } };
268+
269+
NativeMenu.SetMenu(this, appMenu);
270+
}
271+
272+
private static void ShowAboutDialog()
273+
{
274+
var dialog = DialogHelper.CreateTaskDialog(
275+
"Stability Matrix",
276+
$"Version {Compat.AppVersion.ToDisplayString()}"
277+
);
278+
dialog.ShowProgressBar = false;
279+
dialog.FooterVisibility = TaskDialogFooterVisibility.Never;
280+
dialog.Buttons = new List<TaskDialogButton> { TaskDialogButton.CloseButton };
281+
282+
dialog.ShowAsync(true).SafeFireAndForget();
283+
}
284+
223285
/// <summary>
224286
/// Set the default font family for the application.
225287
/// </summary>
@@ -899,16 +961,81 @@ public static void Shutdown(int exitCode = 0)
899961
}
900962
}
901963

964+
private static TaskDialog CreateExitConfirmDialog()
965+
{
966+
var dialog = DialogHelper.CreateTaskDialog(
967+
Languages.Resources.Label_ConfirmExit,
968+
Languages.Resources.Label_ConfirmExitDetail
969+
);
970+
971+
dialog.ShowProgressBar = false;
972+
dialog.FooterVisibility = TaskDialogFooterVisibility.Never;
973+
974+
dialog.Buttons = new List<TaskDialogButton>
975+
{
976+
new("Exit", TaskDialogStandardResult.Yes),
977+
TaskDialogButton.CancelButton,
978+
};
979+
dialog.Buttons[0].IsDefault = true;
980+
981+
return dialog;
982+
}
983+
902984
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
903985
{
904986
Logger.Trace("Start OnShutdownRequested");
905987

906988
if (e.Cancel)
907989
return;
908990

909-
// Skip if Async Dispose already started, shutdown will be handled by it
991+
// Confirm exit while packages are running. This covers every quit path — the window
992+
// close button, ⌘Q, the app menu Quit, and dock Quit — since they all end up here.
993+
if (!isExitConfirmed && !isAsyncDisposeStarted && serviceProvider is not null)
994+
{
995+
var runningPackageService = serviceProvider.GetRequiredService<RunningPackageService>();
996+
if (runningPackageService.RunningPackages.Count > 0)
997+
{
998+
e.Cancel = true;
999+
1000+
// Avoid stacking dialogs if another shutdown request arrives while this one is open
1001+
if (isConfirmingExit)
1002+
return;
1003+
1004+
isConfirmingExit = true;
1005+
Dispatcher
1006+
.UIThread.InvokeAsync(async () =>
1007+
{
1008+
try
1009+
{
1010+
var dialog = CreateExitConfirmDialog();
1011+
if (
1012+
(TaskDialogStandardResult)await dialog.ShowAsync(true)
1013+
== TaskDialogStandardResult.Yes
1014+
)
1015+
{
1016+
isExitConfirmed = true;
1017+
DesktopLifetime?.MainWindow?.Hide();
1018+
Shutdown();
1019+
}
1020+
}
1021+
finally
1022+
{
1023+
isConfirmingExit = false;
1024+
}
1025+
})
1026+
.SafeFireAndForget();
1027+
return;
1028+
}
1029+
}
1030+
1031+
// If an async dispose is already running, cancel until it completes so we don't
1032+
// Environment.Exit before settings/database flushes finish
9101033
if (isAsyncDisposeStarted)
1034+
{
1035+
if (!isAsyncDisposeComplete)
1036+
e.Cancel = true;
9111037
return;
1038+
}
9121039

9131040
// Cancel shutdown for now to dispose
9141041
e.Cancel = true;

StabilityMatrix.Avalonia/Behaviors/ResizeBehavior.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Avalonia.Media;
88
using Avalonia.Xaml.Interactivity;
99
using FluentAvalonia.UI.Controls;
10+
using StabilityMatrix.Avalonia.Helpers;
1011

1112
namespace StabilityMatrix.Avalonia.Behaviors;
1213

@@ -109,7 +110,7 @@ protected override void OnDetaching()
109110

110111
private void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
111112
{
112-
if (e.KeyModifiers != KeyModifiers.Control)
113+
if (e.KeyModifiers != PlatformKeyModifiers.CommandModifier)
113114
return;
114115

115116
if (!UseMouseWheelResize)

StabilityMatrix.Avalonia/Controls/AdvancedImageBoxView.axaml

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,27 @@
44
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
55
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
66
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:gif="clr-namespace:Avalonia.Gif;assembly=Avalonia.Gif"
8+
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
9+
xmlns:markupExtensions="clr-namespace:StabilityMatrix.Avalonia.MarkupExtensions"
710
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
811
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
912
xmlns:models="clr-namespace:StabilityMatrix.Avalonia.Models"
1013
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
11-
xmlns:gif="clr-namespace:Avalonia.Gif;assembly=Avalonia.Gif"
12-
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
1314
d:DataContext="{x:Static mocks:DesignData.SampleImageSource}"
1415
d:DesignHeight="450"
1516
d:DesignWidth="800"
1617
x:DataType="models:ImageSource"
1718
mc:Ignorable="d">
1819
<Grid>
19-
<!-- Tag is not used but sets TemplateKey which is used to select the DataTemplate later -->
20-
<ContentPresenter
21-
Tag="{Binding TemplateKeyAsync^}"
22-
Content="{Binding}">
20+
<!-- Tag is not used but sets TemplateKey which is used to select the DataTemplate later -->
21+
<ContentPresenter Content="{Binding}" Tag="{Binding TemplateKeyAsync^}">
2322
<ContentPresenter.ContentTemplate>
2423
<controls:DataTemplateSelector x:TypeArguments="models:ImageSourceTemplateType">
2524
<DataTemplate x:Key="{x:Static models:ImageSourceTemplateType.WebpAnimation}" DataType="models:ImageSource">
26-
<gif:GifImage
27-
Stretch="Uniform"
28-
SourceUri="{Binding LocalFile.FullPath}"/>
25+
<gif:GifImage SourceUri="{Binding LocalFile.FullPath}" Stretch="Uniform" />
2926
</DataTemplate>
30-
27+
3128
<DataTemplate x:Key="{x:Static models:ImageSourceTemplateType.Image}" DataType="models:ImageSource">
3229
<controls:AdvancedImageBox
3330
CornerRadius="4"
@@ -38,39 +35,40 @@
3835
<ui:MenuFlyoutItem
3936
Command="{x:Static controls:AdvancedImageBoxView.FlyoutCopyCommand}"
4037
CommandParameter="{Binding}"
41-
HotKey="Ctrl+C"
38+
HotKey="{markupExtensions:PlatformGesture Ctrl+C}"
4239
IconSource="Copy"
4340
Text="{x:Static lang:Resources.Action_Copy}" />
4441
<ui:MenuFlyoutItem
4542
Command="{x:Static controls:AdvancedImageBoxView.FlyoutCopyAsBitmapCommand}"
4643
CommandParameter="{Binding}"
47-
HotKey="Shift+Ctrl+C"
48-
IsVisible="{OnPlatform Windows=True, Default=False}"
44+
HotKey="{markupExtensions:PlatformGesture Shift+Ctrl+C}"
45+
IsVisible="{OnPlatform Windows=True,
46+
Default=False}"
4947
Text="{x:Static lang:Resources.Action_CopyAsBitmap}" />
5048
</ui:FAMenuFlyout>
5149
</controls:AdvancedImageBox.ContextFlyout>
5250
</controls:AdvancedImageBox>
5351
</DataTemplate>
54-
52+
5553
<DataTemplate x:Key="{x:Static models:ImageSourceTemplateType.Default}" DataType="models:ImageSource">
56-
<TextBlock
54+
<TextBlock
5755
HorizontalAlignment="Center"
5856
VerticalAlignment="Center"
59-
Text="Unsupported Format"/>
57+
Text="Unsupported Format" />
6058
</DataTemplate>
6159
</controls:DataTemplateSelector>
6260
</ContentPresenter.ContentTemplate>
6361
</ContentPresenter>
6462

65-
<!-- Label pill card -->
63+
<!-- Label pill card -->
6664
<Border
67-
IsVisible="{Binding Label, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
6865
Grid.Row="0"
6966
Margin="4"
7067
HorizontalAlignment="Left"
7168
VerticalAlignment="Bottom"
7269
BoxShadow="inset 1.2 0 10 1.8 #66000000"
73-
CornerRadius="16">
70+
CornerRadius="16"
71+
IsVisible="{Binding Label, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
7472
<Border.Resources>
7573
<DropShadowEffect
7674
x:Key="TextDropShadowEffect"

0 commit comments

Comments
 (0)