Skip to content
Merged

v2.16.1 #1665

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
486853c
Improve inference model workflow handling
mohnjiles Jun 10, 2026
cdf9b76
Reference fixed inference issue
mohnjiles Jun 10, 2026
c95962b
Clarify moved model path matching
mohnjiles Jun 10, 2026
495be81
Surface ComfyUI rejection details in Kontext and Qwen providers
mohnjiles Jun 10, 2026
e37913d
Consolidate duplicated ComfyUI plumbing across Image Lab providers
mohnjiles Jun 10, 2026
c8762d4
Apply Gemini review: fail-fast null guards in ComfyProgressReporter ctor
mohnjiles Jun 10, 2026
35a28f5
Show ComfyUI error detail dialog for Image Lab failures
mohnjiles Jun 10, 2026
922d732
Localize folder-mismatch warning and move action strings
mohnjiles Jun 10, 2026
b061b67
Apply Gemini review: use GetStringValue for folder names in warnings
mohnjiles Jun 11, 2026
3777eec
Merge pull request #1268 from ionite34/codex/inference-model-workflow…
mohnjiles Jun 11, 2026
16f65a5
Merge branch 'dev' into consolidate-image-lab-comfy-helpers
mohnjiles Jun 11, 2026
4bad366
Extract ComfyImageGenerationProviderBase template for Image Lab provi…
mohnjiles Jun 11, 2026
ab4f68a
Merge pull request #1269 from ionite34/consolidate-image-lab-comfy-he…
mohnjiles Jun 11, 2026
b40c520
Merge branch 'dev' into image-lab-comfy-error-dialog
mohnjiles Jun 11, 2026
206bc46
Merge pull request #1270 from ionite34/image-lab-comfy-error-dialog
mohnjiles Jun 11, 2026
d3a550e
Fix Image Lab Gemini errors, I2I masks, CivArchive downloads, custom …
mohnjiles Jun 13, 2026
9c791f8
Apply Gemini review: avoid SKImage double-copy, guard directory scan
mohnjiles Jun 13, 2026
a60d08b
Collapse empty title-bar strip on macOS and Linux
mohnjiles Jun 14, 2026
fd0a1ce
Fix blank export when reusing GPU canvas layers on a CPU surface
mohnjiles Jun 14, 2026
0f2856a
Use Cmd (⌘) instead of Ctrl for shortcuts on macOS
mohnjiles Jun 14, 2026
92f35df
Add native macOS application menu
mohnjiles Jun 14, 2026
ffeca4e
Confirm exit on ⌘Q / menu Quit while packages are running
mohnjiles Jun 14, 2026
5c97257
Add changelog entries for macOS UX changes
mohnjiles Jun 14, 2026
19ffc52
Harden exit-confirmation shutdown flow (review feedback)
mohnjiles Jun 14, 2026
4db8e0c
Merge pull request #1271 from ionite34/image-lab-and-inference-fixes
mohnjiles Jun 15, 2026
cc0e83d
Merge branch 'dev' into macos-ux-improvements
mohnjiles Jun 15, 2026
cc150a2
Merge pull request #1272 from ionite34/macos-ux-improvements
mohnjiles Jun 15, 2026
d002488
Add v2.16.1 supporter shoutouts
mohnjiles Jun 15, 2026
bc54de1
Merge pull request #1273 from ionite34/changelog-2.16.1-supporters
mohnjiles Jun 15, 2026
12f8d3d
Merge branch 'main' into release-2.16.1
mohnjiles Jun 15, 2026
b7e8d53
Optimize SkiaExtensions pixel copy with zero-alloc MemoryCopy
mohnjiles Jun 15, 2026
1623c09
Apply Gemini review: bulk-copy contiguous buffer when strides match
mohnjiles Jun 15, 2026
5115ad1
Add changelog entry for pixel-copy perf improvement
mohnjiles Jun 15, 2026
b5ef26d
Merge pull request #1277 from ionite34/optimize-skia-pixel-copy
mohnjiles Jun 16, 2026
3f0db25
Merge pull request #1278 from ionite34/dev
mohnjiles Jun 16, 2026
eab2622
Merge pull request #1276 from ionite34/release-2.16.1
mohnjiles Jun 16, 2026
be4734e
Merge branch 'main' of https://github.com/LykosAI/StabilityMatrix-Dev
mohnjiles Jun 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ 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.16.1
### Added
- 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
- 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
- Added a **native macOS menu bar** with the standard application menu — About Stability Matrix, **Settings… (⌘,)**, and the usual Services / Hide / **Quit (⌘Q)** items
### Changed
- 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
- 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
- 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
- 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)
- 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
- 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
### Fixed
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- Fixed Animagine XL and other SDXL models with "anima" in the name being misdetected as the Anima architecture
- 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
- 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
### Performance
- 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
### Supporters
#### 🌟 Visionaries
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! 💛
#### 🚀 Pioneers
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. 💛

## v2.16.0
### Added
#### New Feature: 🧪 Image Lab - Conversational Image Generation for ComfyUI
Expand Down
129 changes: 128 additions & 1 deletion StabilityMatrix.Avalonia/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using Apizr;
Expand All @@ -12,6 +13,7 @@
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
Expand Down Expand Up @@ -100,6 +102,18 @@ public sealed class App : Application

private bool isOnExitComplete;

/// <summary>
/// True once the user has confirmed exiting while packages are running, so
/// <see cref="OnShutdownRequested"/> doesn't prompt again on the follow-up shutdown.
/// </summary>
private bool isExitConfirmed;

/// <summary>
/// True while the exit confirmation dialog is open, to avoid stacking dialogs if more
/// shutdown requests arrive (e.g. ⌘Q pressed repeatedly).
/// </summary>
private bool isConfirmingExit;

private ServiceProvider? serviceProvider;

[NotNull]
Expand Down Expand Up @@ -140,6 +154,13 @@ public override void Initialize()

SetFontFamily(GetPlatformDefaultFontFamily());

// macOS app menu must be set here (before AppBuilder's AfterSetup creates the menu
// exporter) so it becomes the app menu and Avalonia appends the standard Services/Hide/Quit
if (Compat.IsMacOS && !Design.IsDesignMode)
{
SetupMacOsApplicationMenu();
}

// Set design theme
if (Design.IsDesignMode)
{
Expand Down Expand Up @@ -220,6 +241,47 @@ public override void OnFrameworkInitializationCompleted()
}
}

/// <summary>
/// Sets the native macOS application menu. Avalonia's native backend reads the app menu (the
/// bold app-name menu) from <see cref="NativeMenu"/> attached to the <see cref="Application"/>
/// during AppBuilder's AfterSetup phase, then appends the standard Services / Hide / Quit (⌘Q)
/// items. We add About and Settings… (⌘,) above those. Called from <see cref="Initialize"/>,
/// which runs just before that phase, so it must not be moved any later or Avalonia falls back
/// to its default "About Avalonia" menu.
/// </summary>
[SupportedOSPlatform("macos")]
private void SetupMacOsApplicationMenu()
{
var aboutItem = new NativeMenuItem("About Stability Matrix");
aboutItem.Click += (_, _) => ShowAboutDialog();

var settingsItem = new NativeMenuItem("Settings…")
{
Gesture = new KeyGesture(Key.OemComma, KeyModifiers.Meta),
};
settingsItem.Click += (_, _) =>
Services
.GetRequiredService<INavigationService<MainWindowViewModel>>()
.NavigateTo<SettingsViewModel>();

var appMenu = new NativeMenu { Items = { aboutItem, new NativeMenuItemSeparator(), settingsItem } };

NativeMenu.SetMenu(this, appMenu);
}

private static void ShowAboutDialog()
{
var dialog = DialogHelper.CreateTaskDialog(
"Stability Matrix",
$"Version {Compat.AppVersion.ToDisplayString()}"
);
dialog.ShowProgressBar = false;
dialog.FooterVisibility = TaskDialogFooterVisibility.Never;
dialog.Buttons = new List<TaskDialogButton> { TaskDialogButton.CloseButton };

dialog.ShowAsync(true).SafeFireAndForget();
}

/// <summary>
/// Set the default font family for the application.
/// </summary>
Expand Down Expand Up @@ -899,16 +961,81 @@ public static void Shutdown(int exitCode = 0)
}
}

private static TaskDialog CreateExitConfirmDialog()
{
var dialog = DialogHelper.CreateTaskDialog(
Languages.Resources.Label_ConfirmExit,
Languages.Resources.Label_ConfirmExitDetail
);

dialog.ShowProgressBar = false;
dialog.FooterVisibility = TaskDialogFooterVisibility.Never;

dialog.Buttons = new List<TaskDialogButton>
{
new("Exit", TaskDialogStandardResult.Yes),
TaskDialogButton.CancelButton,
};
dialog.Buttons[0].IsDefault = true;

return dialog;
}

private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
Logger.Trace("Start OnShutdownRequested");

if (e.Cancel)
return;

// Skip if Async Dispose already started, shutdown will be handled by it
// Confirm exit while packages are running. This covers every quit path — the window
// close button, ⌘Q, the app menu Quit, and dock Quit — since they all end up here.
if (!isExitConfirmed && !isAsyncDisposeStarted && serviceProvider is not null)
{
var runningPackageService = serviceProvider.GetRequiredService<RunningPackageService>();
if (runningPackageService.RunningPackages.Count > 0)
{
e.Cancel = true;

// Avoid stacking dialogs if another shutdown request arrives while this one is open
if (isConfirmingExit)
return;

isConfirmingExit = true;
Dispatcher
.UIThread.InvokeAsync(async () =>
{
try
{
var dialog = CreateExitConfirmDialog();
if (
(TaskDialogStandardResult)await dialog.ShowAsync(true)
== TaskDialogStandardResult.Yes
)
{
isExitConfirmed = true;
DesktopLifetime?.MainWindow?.Hide();
Shutdown();
}
}
finally
{
isConfirmingExit = false;
}
})
.SafeFireAndForget();
return;
}
}

// If an async dispose is already running, cancel until it completes so we don't
// Environment.Exit before settings/database flushes finish
if (isAsyncDisposeStarted)
{
if (!isAsyncDisposeComplete)
e.Cancel = true;
return;
}

// Cancel shutdown for now to dispose
e.Cancel = true;
Expand Down
3 changes: 2 additions & 1 deletion StabilityMatrix.Avalonia/Behaviors/ResizeBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Avalonia.Media;
using Avalonia.Xaml.Interactivity;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Helpers;

namespace StabilityMatrix.Avalonia.Behaviors;

Expand Down Expand Up @@ -109,7 +110,7 @@ protected override void OnDetaching()

private void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
if (e.KeyModifiers != KeyModifiers.Control)
if (e.KeyModifiers != PlatformKeyModifiers.CommandModifier)
return;

if (!UseMouseWheelResize)
Expand Down
36 changes: 17 additions & 19 deletions StabilityMatrix.Avalonia/Controls/AdvancedImageBoxView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,27 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:gif="clr-namespace:Avalonia.Gif;assembly=Avalonia.Gif"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:markupExtensions="clr-namespace:StabilityMatrix.Avalonia.MarkupExtensions"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:models="clr-namespace:StabilityMatrix.Avalonia.Models"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:gif="clr-namespace:Avalonia.Gif;assembly=Avalonia.Gif"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
d:DataContext="{x:Static mocks:DesignData.SampleImageSource}"
d:DesignHeight="450"
d:DesignWidth="800"
x:DataType="models:ImageSource"
mc:Ignorable="d">
<Grid>
<!-- Tag is not used but sets TemplateKey which is used to select the DataTemplate later -->
<ContentPresenter
Tag="{Binding TemplateKeyAsync^}"
Content="{Binding}">
<!-- Tag is not used but sets TemplateKey which is used to select the DataTemplate later -->
<ContentPresenter Content="{Binding}" Tag="{Binding TemplateKeyAsync^}">
<ContentPresenter.ContentTemplate>
<controls:DataTemplateSelector x:TypeArguments="models:ImageSourceTemplateType">
<DataTemplate x:Key="{x:Static models:ImageSourceTemplateType.WebpAnimation}" DataType="models:ImageSource">
<gif:GifImage
Stretch="Uniform"
SourceUri="{Binding LocalFile.FullPath}"/>
<gif:GifImage SourceUri="{Binding LocalFile.FullPath}" Stretch="Uniform" />
</DataTemplate>

<DataTemplate x:Key="{x:Static models:ImageSourceTemplateType.Image}" DataType="models:ImageSource">
<controls:AdvancedImageBox
CornerRadius="4"
Expand All @@ -38,39 +35,40 @@
<ui:MenuFlyoutItem
Command="{x:Static controls:AdvancedImageBoxView.FlyoutCopyCommand}"
CommandParameter="{Binding}"
HotKey="Ctrl+C"
HotKey="{markupExtensions:PlatformGesture Ctrl+C}"
IconSource="Copy"
Text="{x:Static lang:Resources.Action_Copy}" />
<ui:MenuFlyoutItem
Command="{x:Static controls:AdvancedImageBoxView.FlyoutCopyAsBitmapCommand}"
CommandParameter="{Binding}"
HotKey="Shift+Ctrl+C"
IsVisible="{OnPlatform Windows=True, Default=False}"
HotKey="{markupExtensions:PlatformGesture Shift+Ctrl+C}"
IsVisible="{OnPlatform Windows=True,
Default=False}"
Text="{x:Static lang:Resources.Action_CopyAsBitmap}" />
</ui:FAMenuFlyout>
</controls:AdvancedImageBox.ContextFlyout>
</controls:AdvancedImageBox>
</DataTemplate>

<DataTemplate x:Key="{x:Static models:ImageSourceTemplateType.Default}" DataType="models:ImageSource">
<TextBlock
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Unsupported Format"/>
Text="Unsupported Format" />
</DataTemplate>
</controls:DataTemplateSelector>
</ContentPresenter.ContentTemplate>
</ContentPresenter>

<!-- Label pill card -->
<!-- Label pill card -->
<Border
IsVisible="{Binding Label, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Grid.Row="0"
Margin="4"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
BoxShadow="inset 1.2 0 10 1.8 #66000000"
CornerRadius="16">
CornerRadius="16"
IsVisible="{Binding Label, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<Border.Resources>
<DropShadowEffect
x:Key="TextDropShadowEffect"
Expand Down
Loading
Loading