Conversation
Only Flux2KleinProvider caught Refit ApiException and showed ComfyUI's node-validation message; Flux Kontext and Qwen Image Edit collapsed the same failures into a generic "Generation failed", hiding the actual problem (wrong encoder, missing file, etc.) from the user. Bring both providers up to parity with the Klein handler. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The three Image Lab local providers (Flux Kontext, Qwen Image Edit, Flux.2 Klein) and their workflow builders carried heavily duplicated ComfyUI plumbing — identical in all three copies. Extract the genuinely shared logic into three helpers in StabilityMatrix.Avalonia/Helpers, removing ~480 lines of duplication while leaving provider-specific logic (model detection, node graphs, per-provider settings) untouched. - ComfyProgressReporter: a disposable that reports the initial "Queued" stage, subscribes to the ComfyTask progress events, dedups updates by (percent, running node), and unsubscribes on dispose. Replaces the per-provider local-function block. - ComfyGenerationHelper: SelectOutputImages (prefer "SaveImage", else first non-empty output), GetMimeTypeForFileName, and Truncate (the three copied provider helpers now share one home). - ComfyWorkflowHelper: GetReferenceImageNames parameterized over the only things that differed (max image count + filename prefix), and ApplyLoras for the LoRA-chaining loop. Also remove the vestigial IsFluxKontextAvailable observable property in BananaVisionPageViewModel — it was set seven times in UpdateProviderStatus but never read (no binding, no consumer). And log a warning when the fire-and-forget InterruptPromptAsync faults instead of swallowing it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ArgumentNullException.ThrowIfNull for the required task/providerId constructor args of the reusable ComfyProgressReporter, matching the existing pattern used elsewhere in the codebase. The other review suggestions (null checks in SelectOutputImages/GetMimeTypeForFileName/ Truncate) were declined: their inputs are statically non-null under nullable reference types at the only call sites (ComfyImage.FileName is `required`, GetImagesForExecutedPromptAsync returns a non-null dictionary, Exception.Message is never null), so the guards would be redundant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Image Lab failures only surfaced a truncated toast/bubble message while Inference opened a JSON detail dialog for the same errors. Carry the raw ComfyUI error body (queue-time ApiException content or execution-time ComfyNodeException JSON) through ImageGenerationResponse and ImageGenerationException so the chat page can open the same DialogHelper.CreateJsonDialog Inference uses. Node-execution errors were previously caught by the generic handler and lost their details entirely; they now also get a short node-name summary in the chat. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the user-facing strings added for the Inference folder-mismatch warning, the one-click model move, and the Image Lab Comfy error dialog out of code and into Languages/Resources.resx so they can be translated like the rest of the UI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keeps the folder-mismatch warning text consistent with every other SharedFolderType display in this file and with the canonical folder naming used by RelativePathFromSharedFolder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…orkflow-fixes [codex] Improve inference model workflow handling
…ders Addresses review feedback that the three Image Lab providers still shared a near-identical GenerateAsync skeleton after the helper extraction — the connection check, prompt queue, cancellation/interrupt registration, output download loop, and four catch arms were copy-pasted in all three. Pull that whole flow into an abstract ComfyImageGenerationProviderBase (template method), mirroring the existing InferenceGenerationViewModel base pattern. Subclasses now supply only: - ProviderId / ProviderName / LogName / MaxInputImages / ProviderPrefix - GetMissingModels(request) — provider model requirements - BuildWorkflow(request) — option parsing + node graph Shared provider-option parsing (custom UNET, LoRAs, dimensions) moves to protected base helpers used by all three. Each provider drops from ~190 lines to ~45-95; net -250 lines. Provider-specific logic stays in the subclasses: Flux.2 Klein keeps its variant-aware model check and Steps/Cfg/ExplicitDimensions options. Because the providers are DI singletons the base stays stateless per request, so Klein re-resolves its UNET in both GetMissingModels and BuildWorkflow; a logSelection:false flag avoids logging the same selection twice. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…omfy-helpers Consolidate duplicated ComfyUI plumbing across Image Lab providers
…ialog Comfy error detail dialog for Image Lab + localize new strings
…encoders Addresses several Inference / Image Lab / CivArchive issues: - Masks (LykosAI#1654, LykosAI#1658): Skia->Avalonia bitmap conversion now copies pixels into a WriteableBitmap instead of wrapping a pointer owned by a disposed SKImage, so enabled clipping masks no longer disappear, fail to preview, or crash on save. MaskEditor disposes both cached render images; SelectImageCard exposes a MaskOverlayImage that refreshes after editing, restore, or size load. - CivArchive downloads (LykosAI#1660): the primary Download button is now a SplitButton offering the inferred folder, existing subdirectories, and a custom folder picker. Install locations are modelled as a typed InstallLocationOption (no more stringly-typed "Models\" round-trip or "Custom..." equality), and the directory scan runs off the UI thread. ModelImportService sanitizes each pattern path segment and drops rooted/traversal input so downloads stay inside the models dir. - Custom UNet encoders (LykosAI#1661): ClipTypes now lists ComfyUI's current DualCLIPLoader values while keeping single-encoder compatibility and migrating legacy "HiDream" casing to "hidream". - Gemini errors (LykosAI#1664): providers return a machine-readable ImageGenerationErrorCode instead of a baked English string, so Image Lab shows the actual invalid-key, billing, quota, or permission error (localized) and explains that Nano Banana needs a paid-tier key with billing enabled. Account/key dialogs are localized, and the add-key dialog now gives step-by-step setup instructions with the AI Studio and Cloud billing links. Adds a debug-only "Gemini error dialogs" panel in Settings to preview each error through the real handlers, plus unit/UI tests for the path sanitization, encoder list, and pixel-ownership conversion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ToAvaloniaBitmap(SKImage) now peeks pixels directly and only falls back to SKBitmap.FromImage for GPU-backed images, removing a redundant full-image copy. - CivArchive install-location enumeration is wrapped so an inaccessible subdirectory no longer fails the whole scan, keeping the default and custom options available. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TitleBarHost row hosts the window icon, title, and caption buttons on Windows, but those are all native (and hidden) on macOS/Linux, leaving an empty ~32px band under the system title bar. Collapse the row height to 0 on non-Windows platforms so content sits directly below the native title bar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On-screen rendering leases a GPU Skia surface, so the persistent layer surfaces are GPU-backed and owned by the render thread. Off-screen export (e.g. saving an Image Lab annotation) composites onto a CPU surface from the UI thread; reusing those GPU surfaces there produced a fully blank image. RenderToSurface now recreates a layer surface when its GPU/CPU backing doesn't match the current target, so exports composite correctly and the next on-screen render simply recreates the GPU surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keyboard shortcuts were hardcoded to Ctrl everywhere, which is wrong on macOS where the command key is ⌘ (Meta). - Add a PlatformGesture markup extension that swaps the Ctrl token for Cmd on macOS, and convert all XAML KeyBinding/HotKey/InputGesture definitions (save/open, generate, undo/redo, copy/paste/cut, tab nav, mask ops) to use it so both the binding and the displayed glyph follow the platform. - Add PlatformKeyModifiers.CommandModifier (Meta on macOS, Control elsewhere) and apply it to code-behind modifier checks: image paste, search focus, paint undo/redo, mask-editor layer ops, and wheel-resize/zoom (which also avoids clashing with the macOS Ctrl+scroll screen-zoom gesture). Completion trigger (Ctrl+Space) and drag-to-copy are intentionally left on Ctrl, matching macOS conventions for those actions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
macOS apps are expected to have a native menu bar with a proper app menu. Avalonia's native backend builds the app menu from NativeMenu.GetMenu(Application) during AppBuilder's AfterSetup phase, then auto-appends the standard Services/Hide/Quit (⌘Q) items. Set our menu in Application.Initialize() — which runs just before that phase — so About Stability Matrix and Settings… (⌘,) appear in the app menu instead of Avalonia's default "About Avalonia" menu. Setting it later (or on the window) is too late and yields the default or a duplicate menu. Text-editing shortcuts (⌘C/V/X/Z/A) are handled natively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The running-package confirmation only fired on the window close button (MainWindow.OnClosing, WindowClosing reason). ⌘Q, the native app menu Quit, and dock Quit route through lifetime shutdown and skipped it, tearing down running packages without warning. Move the confirmation into App.OnShutdownRequested so every quit path is covered, and route the window close button through App.Shutdown() so there is a single confirmation flow. A guard flag prevents a second prompt on the follow-up shutdown after the user confirms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Cancel re-entrant shutdown requests while async dispose is in progress until it completes, so we don't Environment.Exit before settings/database flushes finish. - Guard the exit confirmation with an isConfirmingExit flag so rapid repeated quit requests don't stack multiple dialogs. - Resolve RunningPackageService from the already-non-null serviceProvider instead of the nullable static Services property. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-fixes Fix Image Lab Gemini errors, I2I masks, CivArchive downloads, custom encoders
macOS UX: ⌘ shortcuts, native app menu, and exit confirmation
Add the Visionaries and Pioneers sections for the 2.16.1 release, sourced from the hall-of-fame roster and cross-checked against the v2.16.0 section plus the annual-carryover and name-mapping notes: - Carry over annual patrons absent from the monthly export (Ghislain G, SinthCore, whudunit) so nobody is dropped prematurely. - Use established clean display names for long-standing supporters (Waterclouds, Tigon, [USA]TechDude) and raw handles otherwise. - cusalapapen1481 is the only genuinely new supporter this release; every Pioneer is a returning face, so no new-Pioneer welcome. - Avoid em dashes per house style. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ters Add v2.16.1 supporter shoutouts
CopyPixelRows allocated a managed byte[] and did a double copy (native -> managed -> native) for every scanline, adding GC pressure on full-resolution images. Switch to unsafe Buffer.MemoryCopy for a direct native-to-native copy with no per-row allocation. Also cast the row offset to long to avoid int overflow on very large images, and drop the now-unused System.Runtime.InteropServices import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When sourceRowBytes == destinationRowBytes the pixel buffer is contiguous, so copy the whole thing in a single Buffer.MemoryCopy instead of looping per scanline. Falls back to the per-row copy when strides differ (alignment padding). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Optimize SkiaExtensions pixel copy with zero-alloc MemoryCopy
Release 2.16.1
There was a problem hiding this comment.
Code Review
This pull request refactors local image generation providers into a shared base class, introduces automatic text encoder and VAE selection, adds a misplaced-model warning with a one-click move feature, and improves macOS platform integration with native menus and Command-key shortcuts. It also optimizes bitmap conversions and enhances CivArchive download folder selection. The code review identified several improvement opportunities, including ensuring DirectoryPath.FullPath is used in path calculations, initializing the mask editor's canvas size if the bitmap size is already populated, refactoring prompt interruption to guarantee CancellationTokenSource disposal, using null-safe string comparisons for enum values, and adding defensive null checks for LoRA models.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Added
qwen_3_4borqwen_3_8b+ Flux.2 VAE for Flux.2 Klein,clip_l+t5xxlfor Flux,qwen_3_06b+qwen_image_vaefor Anima). Anything you pick manually is never overriddenChanged
Fixed
NoneCLIP input to ComfyUI; standalone workflows now expose and automatically fill compatible text encoders and VAEssdxl; the selector now includes current CLIP loader values while retaining single-encoder compatibility and migrating legacyHiDreamcasingPerformance
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. 💛