From 1e52c456cfc5955773b696a4bda339500626620a Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 1 Sep 2026 20:03:40 -0700 Subject: [PATCH 01/12] Merge pull request #1379 from ionite34/fix/package-delete-guards Guard package install & shared-folder teardown against deleting user data (#1733) (cherry picked from commit 31a28fddd8d579d1b3a0a5d826d13622ea7a7426) # Conflicts: # CHANGELOG.md --- CHANGELOG.md | 173 ++++++++++++++++++ StabilityMatrix.Avalonia/DialogHelper.cs | 79 ++++++++ .../Dialogs/ConfirmDeleteDialogViewModel.cs | 12 ++ .../ConfirmPackageDeleteDialogViewModel.cs | 28 ++- .../Dialogs/NewOneClickInstallViewModel.cs | 54 +++++- .../PackageManager/PackageCardViewModel.cs | 27 ++- .../PackageInstallDetailViewModel.cs | 10 +- .../Views/Dialogs/ConfirmDeleteDialog.axaml | 41 +++-- .../Dialogs/ConfirmPackageDeleteDialog.axaml | 18 +- StabilityMatrix.Core/Helper/SharedFolders.cs | 22 ++- .../Models/SharedFoldersTests.cs | 84 ++++++++- 11 files changed, 499 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0ecf704..f80b2f7c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,179 @@ 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). +<<<<<<< HEAD +======= +## v2.17.0-pre.1 +### Fixed +- Fixed the **CivArchive browser** returning far fewer results than the CivArchive website — the site's redesign quietly pinned the app's search route to the past quarter, and the renamed result-count field capped infinite scroll at a single page +- Fixed clicking some CivArchive results showing an error instead of the model — the redesign added whole-model search results whose detail pages load through a redirect the app didn't follow +- Fixed CivArchive file results opening in the web browser instead of the in-app details page, and the details page's version dropdown doing nothing — both casualties of a renamed field in the redesigned API +- CivArchive cards without a preview (whole-model and file results) now backfill an image from the model's gallery in the background instead of staying gray placeholders +- Fixed trigger words being cut off on the CivArchive details page — entries that are whole prompt lines now wrap +- CivArchive article results, which have no in-app page, now open in the web browser instead of erroring +- Fixed OneTrainer's **Windows ROCm** bitsandbytes 8-bit optimizers, disabled in the last release ([#1708](https://github.com/LykosAI/StabilityMatrix/issues/1708)) — a wheel pinned to match OneTrainer's own bitsandbytes 0.49.1 requirement replaces the incompatible 0.50 build, so 8-bit optimizers work again - thanks to @0xDELUXA! +- Installing a package into a folder that already contains files — like an existing ComfyUI installation placed in `Data\Packages` to be imported — now shows the folder's total size and file count and requires explicit confirmation, instead of silently deleting everything in it; this includes the first-run one-click installer ([#1733](https://github.com/LykosAI/StabilityMatrix/issues/1733)) +- Turning off shared model folders no longer tries to delete real directories at the link locations — only links created by Stability Matrix are removed, so the model folders of an imported package are never touched ([#1733](https://github.com/LykosAI/StabilityMatrix/issues/1733)) +- The package uninstall confirmation now correctly lists models/checkpoints among the items that will be deleted for packages using config-based model sharing (ComfyUI's default) — previously it implied they were safe — and shows the package folder's total size + +## v2.17.0-dev.3 +### Added +#### New Feature: 🔀 CivitAI Workflow Browsing +- The **Model Browser** now browses and imports **ComfyUI workflows** from CivitAI — pick the new **Workflows** model type, open one, and import it straight into your library; archives unpack automatically, and images with an embedded workflow become regular workflows +- Your workflow library is now **linked into ComfyUI itself** — imported workflows appear under a "Stability Matrix" folder in ComfyUI's own workflow browser, for every installed ComfyUI-based package +- **Custom node install assist**: after importing a workflow that needs custom nodes, Stability Matrix reads the node packs out of the workflow file, shows which you already have, and offers one-click installs for the rest — also on any installed workflow via **Check Required Nodes** +- The Workflows page groups your library **by folder**, with a folder tree to jump between them +- Model Browser cards show **Installed** / **Update Available** for imported workflows, and the details page can delete one again +- New **Edit Metadata** on workflow cards — rename, describe, and give any workflow a picture, including files you dropped in yourself years ago +#### New Feature: 🎨 Qwen Image & Krea 2 in Inference +- New workflow profiles with auto-detection, automatic text encoder + VAE pairing, and one-click recommended sampler settings (both need an up-to-date ComfyUI, so update your package if a workflow is rejected): + - **Qwen Image** (2512) — the top-ranked open text-to-image model, with best-in-class text rendering + - **Krea 2 Turbo** (8-step) and **Krea 2 Raw** — Krea's open-weights model, free for personal use under the Krea AI Community License +- Qwen-tagged CivitAI checkpoints now route to the DiffusionModels folder like Wan/Flux/Krea already did +#### New Feature: ⬇️ One-Click Text Encoder & VAE Downloads +- When your model needs a text encoder or VAE you don't have, the Model card says what's missing and offers the official download in one click — Flux, Flux.2 (4B/9B-aware), Z-Image, Anima, HiDream, Qwen Image, and Krea 2 +- Only official shared components are offered, so any CivitAI fine-tune still works; progress shows in the card and finished files select themselves +#### New Feature: 🧩 Persistent Inference Layouts +- Rearranged panes in Inference now stick, one of our most-requested features on Discord, GitHub ([#1340](https://github.com/LykosAI/StabilityMatrix/issues/1340)), and our feature tracker ([1](https://lykos.ai/feature/8f55f8d5-c25a-437f-a2ba-4093e3984754), [2](https://lykos.ai/feature/c407cc98-e355-4e8d-98a5-23be854ed6c0)) alike — arrangements are kept per tab type across restarts, **saving a project** stores its layout in the `.smproj`, and **Restore Default Layout** resets the current tab in place +- Added **uv cache** management under **Settings → Package Environment** ([feature request](https://lykos.ai/feature/4dc8ab9d-e35f-436b-9a3f-d3abc2db1800)) — see the cache size and clear it with progress in **Activity**, plus an optional size limit and auto-cleanup toggle +- Downloads in the **Activity** popout now show downloaded and total size (e.g. 1.2 GB / 6.5 GB) next to the percentage and speed ([feature request](https://lykos.ai/feature/f571997c-3607-48db-a62f-b315fc733280)) +- Model downloads now skip files you already have ([feature request](https://lykos.ai/feature/3dc89d55-4859-4484-bde9-5951ad87a48a)) — a hash match points you at the existing file instead of downloading a duplicate +- Added a **Quick-Hide Hotkey** for shared computers ([feature request](https://lykos.ai/feature/3dbfa0e1-650a-426a-906d-b81d6f414104)) — instantly minimize, or exit with no confirmation prompts. Off by default; bind it in **Settings → System** +- Added **Sort** options to the Inference model picker — file name, title, base model, type, size, and dates, remembered between sessions; search relevance still wins while you type +### Changed +- The **Workflows** page is now your installed workflow library, front and center — no more tabs, since browsing moved to the Model Browser. Cards for CivitAI-imported workflows link back to their CivitAI page +- Removed the **OpenArt workflow browser**: OpenArt has discontinued its ComfyUI workflow community. Workflows you already imported stay fully usable in your library +- Updated **AI-Toolkit** installs to PyTorch 2.13.0 on CUDA 13.0 with Python 3.12, matching current upstream requirements ([#1714](https://github.com/LykosAI/StabilityMatrix/issues/1714)) — with a console warning at launch if your NVIDIA driver predates the 580 series +- Updated the bundled **uv** package manager to 0.12.5, adding newer Python builds to the Python picker — including the 3.10 security releases requested in [#1709](https://github.com/LykosAI/StabilityMatrix/issues/1709) +- Refreshed the model card badges across the **Checkpoint Manager** and the **CivitAI / CivArchive browsers** — compact chips instead of chunky pills, an update badge showing the release date, a distinct style for early-access-only updates, and clearer Installed / NSFW / Deleted states +- **Checkpoint Manager** search is now typo-tolerant, sharing the Inference picker's fuzzy matching (`#tag` search unchanged) +- **Triton & SageAttention** installs now pick up the latest wheels again — an upstream naming change had pinned them to older builds; the newest wheels fix black or noisy outputs on some setups +### Fixed +- Fixed custom node installs failing on legacy source builds that import `pkg_resources` (e.g. `cupy-wheel`) — extension install scripts now get the same `setuptools<82` constraint package installs already had +- Fixed Qwen Image Edit's model check accepting an incompatible Krea 2 text encoder; the correct 7B download is offered instead +- Fixed cloud-download entries in model dropdowns doing nothing until you clicked another item first, and the jumpy scrolling around them +- Fixed switching model families after a restart keeping the previous family's encoders and slot count +- Fixed companion downloads sometimes not filling their slot when the batch finished +- Fixed the Model Browser failing with "CivitAI can't be reached right now (OK: OK)" — CivitAI started returning `null` for some model statistics; missing stats now read as 0 +- Fixed [#1695](https://github.com/LykosAI/StabilityMatrix/issues/1695) - models whose CivitAI page has since been deleted showing an error dialog on every click, with no way to break the link: + - The details page falls back to your cached info, and **Next/Previous** skip deleted models instead of getting stuck on an error + - A new right-click **Disconnect from Source** severs the dead link while keeping local metadata (name, description, thumbnail, trigger words) +- Fixed [#1708](https://github.com/LykosAI/StabilityMatrix/issues/1708) - OneTrainer on **Windows ROCm** crashing at the start of training with bitsandbytes 8-bit optimizers — the incompatible build is no longer installed, with a launch notice explaining why - thanks to @NeuralFault for the diagnosis! +- Fixed [#1710](https://github.com/LykosAI/StabilityMatrix/issues/1710) - CivitAI downloads sometimes fetching a different file from the same model version (then failing hash verification) when several files share identical metadata +- Fixed [#1703](https://github.com/LykosAI/StabilityMatrix/issues/1703) - model cover/preview images not displaying when your **File Name Pattern** contains a `#` +- Fixed [#1705](https://github.com/LykosAI/StabilityMatrix/issues/1705) - the **Model Browser** showing results in a scrambled order after refreshing with the **Search** button +- Fixed [#1715](https://github.com/LykosAI/StabilityMatrix/issues/1715) - the app crashing on startup on Linux when the models folder holds names differing only in case (e.g. `unet` and `Unet`, also [#1149](https://github.com/LykosAI/StabilityMatrix/issues/1149)/[#1357](https://github.com/LykosAI/StabilityMatrix/issues/1357)) — those folders now show up in the Checkpoint Manager instead of being silently skipped +- Fixed [#1707](https://github.com/LykosAI/StabilityMatrix/issues/1707) - Inference's **Layer Diffuse** addon failing at the sampler with `'NoneType' object is not subscriptable` on current ComfyUI — Inference offers the Inference Core Nodes update that carries the fix +- Fixed [#1717](https://github.com/LykosAI/StabilityMatrix/issues/1717) - Inference and Image Lab asking you to install a ComfyUI extension you already have (most often **ComfyUI-GGUF**), with the prompt returning after every install — registry installs, remotes differing only by a `.git` suffix or SSH form, and Windows-style git config line endings are all recognized now +- Fixed [#1716](https://github.com/LykosAI/StabilityMatrix/issues/1716) - phantom **Update Available** badges on embeddings, VAEs, upscalers, and ControlNets; wrong badges clear on the next check +- Fixed early access versions no longer being detected, breaking the early-access badge and the **Hide Early Access Models** filter +- Fixed multi-architecture models (e.g. Illustrious | Pony | NoobAI in one listing) counting a release for a different architecture as an update — checks now compare within your installed version's base model +- Fixed metadata scans always reporting success — the completion message now shows real counts, tells rate limiting apart from "not on CivitAI", and stops early instead of grinding your library into a rate limit +- Fixed **Update Metadata** skipping models imported from HuggingFace or OpenModelDB (now hashed on demand), and scans freezing the app on large files +- Fixed **Organize Models** appearing to do nothing out of the box — the default pattern is now `{base_model}/{file_name}` so the first preview proposes real folders (saved patterns untouched) +- Fixed model card text being hard to read in light theme +- Fixed the app sometimes crashing on exit with an **Inference** tab open, most often with **Remove folder links on shutdown** enabled +- Fixed a crash dialog appearing after you'd already closed the app — shutdown errors are now logged and the app just exits +- Fixed installing a package that needs a newer Python breaking an already-installed package's environment — a 3.13 package alongside an existing 3.12 one left the older one unable to launch with `error no: 2`, and hand-repairs were overwritten on every launch - thanks to @NeuralFault! +- Fixed **Clear Finished** in the **Activity** popout also clearing failed downloads, taking their **Retry** button with them — failed items now stay until you retry or dismiss them - thanks to @NeuralFault! +- Fixed **SwarmUI**'s **Launch Mode** option having no effect, as it used a hyphen where SwarmUI expects `--launch_mode` — the dropdown also gains a **none** option to skip the browser launch - thanks to @NeuralFault! +- Fixed **Forge Neo** and other uv-dependent packages failing to launch on **Linux** and **macOS** with `Error: 2 uv not found` ([#1713](https://github.com/LykosAI/StabilityMatrix/issues/1713)) — the bundled uv is now on the launch PATH there too - thanks to @NeuralFault! +### Supporters +#### 🌟 Visionaries +Three new model profiles, one-click downloads for the pieces that go with them, workflows you can browse and import from CivitAI, and Inference layouts that finally stay where you put them: this build is mostly people asking for things and us getting to say yes. Our Visionaries are why that's possible at all, so thank you **Waterclouds**, **MrMxyzptlk12836**, **bluepopsicle**, **Ibixat**, **Droolguy**, **snotty**, **dispenser**, **cusalapapen1481**, **moon_milky2843**, **CC**, and **SnooSnooEternal**. Every one of these features has a bit of your backing in it. And a warm hello to **TwistedDragon**, **Eaglebane**, and **hasezou11013179**, who join the Visionaries this time around; we're really glad you're here. 💛 + +## v2.17.0-dev.2 +### Added +#### New Feature: 📋 Inference Prompt Queue +- Queue up multiple generations from any Inference tab and run them one after another — a long-requested feature ([#1622](https://github.com/LykosAI/StabilityMatrix/issues/1622)): + - **Add to Queue** sits next to Generate (and in the tab's overflow menu) in every generation tab — Text-to-Image, Image-to-Image, Flux, Upscale, and the video tabs. The Generate button itself is unchanged; queueing is a separate action + - Queue items store the full project state, not a baked workflow — **Open in tab** re-opens any item as a real Inference tab for tweaking and re-queueing + - Cards show a prompt snippet, compact parameters, a status badge, live progress and preview while running, and the finished thumbnail when done (click it to open the full image viewer) + - Manage the queue freely: reorder, remove, cancel the running item, **re-queue** finished/failed/cancelled items (or re-queue all finished), and clear finished or pending items + - **Start** auto-connects to ComfyUI if it's already running, or shows the usual launch prompt if it isn't; **Pause** finishes the current item before stopping + - The queue is saved to your library folder and survives restarts — interrupted items come back as Pending, and finished items keep their thumbnails +#### New Feature: 📖 In-App Documentation +- Read the Stability Matrix guides without leaving the app: + - Press **F1** from anywhere, or open it from **Settings → About → Documentation** + - New **?** buttons on the Package Manager, install details, the running package console, Inference, and the Environment Variables and App Folders settings open the page for what you're looking at + - Browse every section from a nav tree, follow links between pages, and zoom the text in or out — your zoom level is remembered + - Pages are read live from [docs.lykos.ai](https://docs.lykos.ai/stability-matrix/), so new and updated writing shows up without an app update, and a copy ships inside the app so it still works offline +- Added a **What's New** viewer — browse release notes for any version right in the app from **Settings → About**, with a one-time heads-up after each update (can be turned off in update settings) +- 📚 **New documentation site** at [docs.lykos.ai](https://docs.lykos.ai/stability-matrix/) — getting started and installation guides, package manager and Inference walkthroughs, environment variable and advanced configuration references, a terminology glossary, and troubleshooting for common issues. Written by @NeuralFault! +- Added **OneTrainer** to the native **Windows ROCm (AMD GPU)** helper — new OneTrainer installs on supported AMD hardware get the ROCm PyTorch build, ROCm-aware bitsandbytes and triton dependencies, and the right launch environment applied automatically. New OneTrainer installs also default to Python 3.12 on all platforms - thanks to @NeuralFault! +- Added Inference support for **IPAdapter** — guide a generation with a reference image alongside your prompt, for style, composition, or subject consistency without training a LoRA. Add it from the sampler's **Addons** section in any generation tab, right alongside ControlNet: + - Drop in a reference image, then pick an **IPAdapter model** and its matching **CLIP Vision** encoder — both dropdowns can download the files for you if you don't have them yet + - **Weight Type** chooses how the reference gets applied, from plain `linear` blending through the `style transfer` and `composition` modes that separate a reference's look from its layout + - **Control Weight** and **Control Steps** set how strongly the reference applies and over which portion of the generation, matching the controls on the ControlNet card + - Applies to every model loaded in the workflow, so base and refiner are both conditioned on the reference +### Changed +- CivitAI downloads now pick their destination folder from the file's declared type (**Diffusion Model**/**UNet** → DiffusionModels, **Text Encoder** → TextEncoders, **CLIP Vision** → ClipVision, **ControlNet** → ControlNet, **Upscaler** → upscalers) instead of guessing from the model's name. Name-based guessing remains only for files typed plain "Model", and now recognizes **Krea 2** checkpoints as UNet-only +- Updated the **Windows ROCm helper**'s bundled bitsandbytes wheel to a build compatible with ROCm 7.13–7.15, so it keeps working as AMD's ROCm Technical Preview builds update - thanks to @NeuralFault! +### Fixed +- Fixed a class of random crashes in the Inference **mask editor** and **image annotation editor** — undo/redo, layer operations, exporting, or closing the editor could free graphics resources that were still being drawn with, occasionally crashing mid-stroke or while saving. Canvas rendering has been restructured so this can't happen +- Fixed **pen pressure** re-widening the whole stroke instead of following the pen while drawing, and mouse-drawn strokes coming back ~25% thicker after saving and reopening a project. Existing project files load exactly as before +- Fixed the mask editor and image annotation editor leaking graphics memory — the paint canvas wasn't released on close, and paint-bucket fills were never freed +- Fixed fast brush strokes occasionally failing with a "collection was modified" error while the stroke was still being drawn +- Fixed opening older projects whose masks contained stroke points outside the canvas failing with an overflow error +- Fixed a potential crash when the paint canvas rendered before its size was set +- Fixed CivitAI models showing an empty Files section with no download links when their files use CivitAI's newer type labels (e.g. **Krea 2 Turbo** and **Z-Image**, typed **Diffusion Model** or **Text Encoder**). All current CivitAI file types are now recognized across the browser, details page, version dialog, bulk download, and installed/update detection +- Fixed CivitAI "Download with Stability Matrix" links saving UNet-only checkpoints (Flux, Wan Video, Hunyuan, Krea 2) into the **StableDiffusion** folder — external links now use the same destination logic as the in-app browser +- Fixed [#1668](https://github.com/LykosAI/StabilityMatrix/issues/1668) - the **Output Browser** crashing on open when an output folder contained a broken junction or symlink (a folder whose target no longer exists); those entries are now skipped instead of taking the page down +- Fixed [#1681](https://github.com/LykosAI/StabilityMatrix/issues/1681) - Inference generation with **FaceDetailer** failing instantly with "An item with the same key has already been added" when two installed custom node folders share the same git remote +- Fixed [#1679](https://github.com/LykosAI/StabilityMatrix/issues/1679) - model details pages never showing the **Installed** label, delete button, or version checkmark for files whose type displays as "Unknown". Installed detection now goes by file hash, so it works no matter what type CivitAI reports +- Fixed [#1667](https://github.com/LykosAI/StabilityMatrix/issues/1667) - importing an existing package folder silently pre-selecting **Forge** as the package type, misclassifying re-imported packages. The type is now auto-detected from the folder's git remote +- Fixed [#1669](https://github.com/LykosAI/StabilityMatrix/issues/1669) - **Stable Diffusion WebUI reForge** installing the CUDA build of PyTorch on Linux AMD systems even with ROCm selected +- Fixed [#1672](https://github.com/LykosAI/StabilityMatrix/issues/1672) - **Image Lab** failing with a generic "ComfyUI rejected the workflow" error when generating with a GGUF model while the **ComfyUI-GGUF** extension isn't installed — it now offers the same one-click **install and restart** prompt as Inference +- Fixed GGUF-quantized **text encoders** failing with a ComfyUI error when selected — they now load through the ComfyUI-GGUF CLIP loaders in all Inference workflows and Image Lab (mixed GGUF + safetensors selections work too), with the install prompt appearing if the extension is missing +- Fixed **Chroma** and other single-encoder models being impossible to configure in Inference UNet workflows — the encoder **Type** dropdown was missing `chroma` and other single-encoder types, and the closest option (`flux`) demanded a second encoder these models don't use. The missing types are now listed, and single-encoder types get a single encoder slot +- Fixed GGUF text encoders in the shared **TextEncoders** folder not appearing in the Inference Text Encoder dropdowns while connected to ComfyUI +- The **HuggingFace browser** now suggests the TextEncoders folder for recognizable text encoder files (`byt5`/`mt5`/`llama`/`gemma`/`qwen2` prefixes, `text-encoder`/`enconly` names) instead of defaulting every `.gguf` to DiffusionModels +- Fixed [#1682](https://github.com/LykosAI/StabilityMatrix/issues/1682) - the Linux **AppImage** failing to start on modern distros (Ubuntu 24.04+, Fedora 40+) that no longer ship `libfuse2`. The AppImage now uses a FUSE3-based runtime, and falls back to extract-and-run when FUSE isn't available at all - thanks to @NeuralFault! +- Fixed **OneTrainer** failing to launch with current upstream versions after its UI script was renamed (`train_ui.py` → `train_ui_ctk.py`) - thanks to @NeuralFault! +### Performance +- Dragging an image layer with the **Move** tool in the layered mask editor now re-renders only the dragged layer instead of re-compositing every layer on each pointer move, so dragging stays smooth in multi-layer projects +- Faster color-mask extraction for regional prompting — a 1024×1024 canvas with six regions previously did over six million dictionary lookups per extraction +- Fixed a small native memory leak while drawing with the mouse (a path object per frame was never freed), and removed a redundant full-canvas scan after every paint-bucket fill +### Security +- Bundled **ADetailer** model downloads now point at a fixed Hugging Face revision instead of the repository's moving `main` branch, so an upstream re-upload can't change what you get - thanks to @ungrav! +### Supporters +#### 🌟 Visionaries +The prompt queue has been one of our most requested features ever, and builds like this only happen because our Visionaries give us the freedom to take them on. Thank you **Waterclouds**, **MrMxyzptlk12836**, **Psilocyfer18731**, **bluepopsicle**, **Ibixat**, **Droolguy**, **KalAbaddon**, **LG**, **snotty**, **whudunit**, **cusalapapen1481**, **moon_milky2843**, **SkynetFuture**, and **sn3232323233350** for standing behind us build after build. And to our three newest Visionaries, **tarekk071223**, **CC**, and **SnooSnooEternal**: welcome aboard, it means the world to have you with us. 💛 + +## v2.17.0-dev.1 +### Added +#### New Feature: 🤗 Live HuggingFace Model Browser +- Reworked the HuggingFace tab into a full browser instead of a fixed list of links — the curated picks are still there as the default view: + - Search HuggingFace for models right from the tab, sorted by downloads, likes, or most recently updated + - Paste a repository link to browse all of its files directly + - Browse files as a flat list or a folder tree, with a quick name filter and a **Hide installed** toggle + - Choose where each file goes (auto-detected and editable), or send a whole multi-file model such as a diffusers repo into one folder with its structure intact + - Select multiple files at once and see the total download size before you start, with a warning if there isn't enough free space + - Gated or private repositories work once you add a HuggingFace token in **Settings → Accounts** +- Added Inference support for the **Wan 2.2 14B** mixture-of-experts video models. Enable **Dual expert (high / low noise)** from the Wan model card's options menu (⚙️) to load a second low-noise expert alongside the high-noise model; generation then runs the two-pass high-noise → low-noise sampling the 14B architecture expects, switching at a configurable **Boundary** (the fraction of steps handled by the high-noise expert, default 0.5). Works in both Wan Text to Video and Image to Video, and leaving the low-noise slot empty keeps the existing single-model Wan behavior unchanged + - When you pick a model that looks like one half of a 14B expert pair (e.g. `wan2.2_t2v_high_noise_14B`), the card offers a one-click prompt to enable the second expert and auto-selects its matching low-noise counterpart + - The high-noise and low-noise model pickers sit together at the top as a labeled pair, with Precision, VAE, Text Encoder and Shift tucked into an **Advanced** section to keep the card approachable + - Added a separate **Low-Noise LoRAs** list so per-expert speed LoRAs (such as Wan 2.2-Lightning's high/low-noise pair) land on the correct model — the **High-Noise LoRAs** list applies to the high-noise/primary model, and the new Low-Noise LoRAs list applies to the low-noise expert + - Added hover tooltips to the Wan model card fields (Precision, VAE, Text Encoder, Shift, Low-Noise, Boundary) explaining what each one does +- Added a **gallery picker** for the Inference "+" new-tab button, grouping the project types into **Image / Video / Legacy** sections with icons and descriptions instead of a flat dropdown. The redundant standalone Flux text-to-image and SVD image-to-video types now live under **Legacy** so existing projects still open, without cluttering the list for new users + - Prefer the old menu? A **Compact New Tab Menu** toggle under Settings → Inference (and a quick link at the bottom of the picker dialog) switches the "+" button back to the fast dropdown +### Changed +- **Windows builds are now code-signed.** The portable executable is signed with a verified certificate, so Windows SmartScreen no longer flags Stability Matrix as from an unknown publisher. You may still see a SmartScreen prompt on the first releases while the certificate builds reputation, but those warnings will taper off as more people run signed builds +- **Generate now auto-resumes after launching ComfyUI.** If you press Generate in Inference while ComfyUI isn't connected and choose to launch it from the connection prompt, the queued generation now waits for startup and runs on its own once connected - no need to press Generate a second time +### Fixed +- Fixed the Inference **VAE** dropdown listing models in a seemingly random order, and the **Text Encoder** / **CLIP Vision** dropdowns occasionally reordering as the list finished loading; all three now sort alphabetically, with **Default** pinned to the top of the VAE list +- Model dropdowns no longer reserve space for a thumbnail when the selected model has no preview image, so names are no longer squished into a narrow strip +- Fixed [#1666](https://github.com/LykosAI/StabilityMatrix/issues/1666) - AppImage builds creating a broken `.desktop` entry (`NoDisplay=true`, missing icon) that never showed up in the application launcher and reverted any manual edits on the next launch. AppImage runs now write a correct `.desktop` entry with the extracted app icon so Stability Matrix appears in your launcher/menu, and report a matching `WM_CLASS` so the running window shows the Stability Matrix icon in the dock/taskbar instead of a generic one. Only applies to AppImage runs; deb/rpm/flatpak installs keep their package-managed entries +- Fixed `stabilitymatrix://` deep links (e.g. CivitAI "Download with Stability Matrix" buttons) being ignored on Linux AppImage builds — the URI is now forwarded to the running instance and the download starts, instead of just opening another window +### Performance +- The **Checkpoints** and **Outputs** galleries now load thumbnails at display size instead of full resolution, using far less memory and scrolling much more smoothly with large libraries +- The **CivitAI** and **OpenModelDB** browsers now keep card images in memory, so scrolling back over models you've already seen no longer re-loads them from disk +- Lightened the CivitAI model cards so they render faster while scrolling +### Supporters +#### 🌟 Visionaries +This build leans hard into what's next: a live HuggingFace browser and Wan 2.2 video generation. None of that exploration happens without our Visionaries giving us the room to chase it. So a huge thank you to **Waterclouds**, **MrMxyzptlk12836**, **Psilocyfer18731**, **bluepopsicle**, **Ibixat**, **Droolguy**, **KalAbaddon**, **LG**, **snotty**, **whudunit**, **cusalapapen1481**, and **moon_milky2843**. You make the experiments possible. And a big hello to **SkynetFuture** and **sn3232323233350**, who join the Visionary crew this time around. We're genuinely glad you're here. 💛 + +>>>>>>> 31a28fdd (Merge pull request #1379 from ionite34/fix/package-delete-guards) ## v2.16.3 ### Added #### New Feature: 🔀 CivitAI Workflow Browsing diff --git a/StabilityMatrix.Avalonia/DialogHelper.cs b/StabilityMatrix.Avalonia/DialogHelper.cs index 90f3e50a9..22c581a30 100644 --- a/StabilityMatrix.Avalonia/DialogHelper.cs +++ b/StabilityMatrix.Avalonia/DialogHelper.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; using System.Threading.Tasks; +using AsyncAwaitBestPractices; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Documents; @@ -20,19 +22,25 @@ using AvaloniaEdit.TextMate; using CommunityToolkit.Mvvm.Input; using FluentAvalonia.UI.Controls; +using Microsoft.Extensions.DependencyInjection; using NLog; using Refit; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Helpers; using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Models; +using StabilityMatrix.Avalonia.Services; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Core.Exceptions; using StabilityMatrix.Core.Extensions; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Services; using TextMateSharp.Grammars; using Process = FuzzySharp.Process; +using Size = StabilityMatrix.Core.Helper.Size; namespace StabilityMatrix.Avalonia; @@ -638,6 +646,77 @@ public static TaskDialog CreateTaskDialog(string title, string description) XamlRoot = App.VisualRoot, }; } + + /// + /// Shows a confirmation dialog before deleting existing files at a package install location, + /// e.g. a folder the user placed there manually to import an existing installation. + /// Returns true if the location does not exist, is empty, or the user explicitly confirmed + /// the deletion; false if the user cancelled. + /// + public static async Task ConfirmDeleteExistingInstallDataAsync(DirectoryPath installLocation) + { + if (!installLocation.Exists || !installLocation.Info.EnumerateFileSystemInfos().Any()) + return true; + + var vmFactory = App.Services.GetRequiredService>(); + + var vm = vmFactory.Get(); + vm.Title = "Existing files found"; + vm.Description = + "This folder already exists and contains files that were not installed by Stability Matrix. " + + "Installing here will permanently delete the folder shown below and everything inside it.\n\n" + + "If this is an existing installation you want to keep, choose Cancel and use Import " + + "from the Packages page instead, or move your data elsewhere first."; + vm.PathsToDelete = [installLocation.FullPath]; + vm.IsRecycleBinAvailable = false; + vm.TotalSizeText = "Calculating size…"; + + Task.Run(() => + { + var (fileCount, totalBytes) = GetFileStats(installLocation.Info); + Dispatcher.UIThread.Post(() => + vm.TotalSizeText = + $"Total: {Size.FormatBytes(Convert.ToUInt64(totalBytes))} in {fileCount:N0} files" + ); + }) + .SafeFireAndForget(ex => + { + Logger.Warn(ex, "Failed to calculate size of {Path}", installLocation.FullPath); + Dispatcher.UIThread.Post(() => vm.TotalSizeText = null); + }); + + return await vm.GetDialog().ShowAsync() == ContentDialogResult.Primary; + } + + /// + /// Gets the total file count and size of a directory, without following symbolic links / junctions. + /// + private static (int FileCount, long TotalBytes) GetFileStats(DirectoryInfo directory) + { + var fileCount = 0; + var totalBytes = 0L; + + foreach (var file in directory.EnumerateFiles()) + { + if (file.Attributes.HasFlag(FileAttributes.ReparsePoint)) + continue; + + fileCount++; + totalBytes += file.Length; + } + + foreach (var subDir in directory.EnumerateDirectories()) + { + if (subDir.Attributes.HasFlag(FileAttributes.ReparsePoint)) + continue; + + var (subCount, subBytes) = GetFileStats(subDir); + fileCount += subCount; + totalBytes += subBytes; + } + + return (fileCount, totalBytes); + } } // Text fields diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmDeleteDialogViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmDeleteDialogViewModel.cs index 5da0e0300..1823024c0 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmDeleteDialogViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmDeleteDialogViewModel.cs @@ -21,6 +21,18 @@ public partial class ConfirmDeleteDialogViewModel(ILogger + /// Optional description shown below the title, for extra context on what is being deleted and why. + /// + [ObservableProperty] + private string? description; + + /// + /// Optional total size / file count line shown below the paths list. + /// + [ObservableProperty] + private string? totalSizeText; + [ObservableProperty] [NotifyPropertyChangedFor(nameof(ConfirmDeleteButtonText))] [NotifyPropertyChangedFor(nameof(IsPermanentDelete))] diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmPackageDeleteDialogViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmPackageDeleteDialogViewModel.cs index 8a6a2c37a..cb60cb047 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmPackageDeleteDialogViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/ConfirmPackageDeleteDialogViewModel.cs @@ -4,7 +4,9 @@ using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.FileInterfaces; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; @@ -21,6 +23,9 @@ public partial class ConfirmPackageDeleteDialogViewModel : ContentDialogViewMode [NotifyPropertyChangedFor(nameof(IsValid))] public partial string PackageName { get; set; } = string.Empty; + [ObservableProperty] + public partial string? FolderSizeText { get; set; } + public string? ExpectedPackageName => Package.DisplayName; public bool IsValid => ExpectedPackageName?.Equals(PackageName, StringComparison.Ordinal) ?? false; public string DeleteWarningText @@ -36,7 +41,9 @@ public string DeleteWarningText if (!Package.UseSharedOutputFolder) items.Add("• Images/outputs"); - if (Package.PreferredSharedFolderMethod is SharedFolderMethod.None) + // Symlink mode is the only one that relocates models out of the package folder; + // in Configuration (yaml) and None modes they are real files that will be deleted + if (Package.PreferredSharedFolderMethod is not SharedFolderMethod.Symlink) items.Add("• Models/checkpoints placed in the package's model folders"); items.Add("• Any custom files in the package folder"); @@ -50,4 +57,23 @@ private async Task CopyExpectedPackageName() { await App.Clipboard?.SetTextAsync(ExpectedPackageName); } + + /// + public override async Task OnLoadedAsync() + { + await base.OnLoadedAsync(); + + if (Package.FullPath is not { } fullPath) + return; + + try + { + var sizeBytes = await new DirectoryPath(fullPath).GetSizeAsync(includeSymbolicLinks: false); + FolderSizeText = $"Total size: {Size.FormatBytes(Convert.ToUInt64(sizeBytes))}"; + } + catch (Exception) + { + // Size display is informational only + } + } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs index 596ad32ff..2997c5871 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/NewOneClickInstallViewModel.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using AsyncAwaitBestPractices; +using Avalonia.Controls.Notifications; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -121,9 +122,49 @@ private async Task InstallComfyForInference() [RelayCommand] private async Task InstallPackage(BasePackage selectedPackage) { - OnPrimaryButtonClick(); - var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", selectedPackage.Name); + var installPath = new DirectoryPath(installLocation); + + // In the first-run flow the progress dialog is opened when the recommended-models dialog + // closes; the existing-data path skips that dialog, so it opens the progress dialog itself + var showProgressDialogOnStart = false; + + if (installPath.Exists && installPath.Info.EnumerateFileSystemInfos().Any()) + { + // The install location already holds data we didn't put there — likely an existing + // installation the user placed for Import. Close this dialog first so the + // confirmation is the only dialog on screen, then require explicit confirmation + // before deleting anything. + OnCloseButtonClick(); + + if (!await DialogHelper.ConfirmDeleteExistingInstallDataAsync(installPath)) + { + notificationService.Show( + "Installation cancelled", + $"Existing files at {installLocation} were left untouched.", + NotificationType.Information + ); + return; + } + + await installPath.DeleteVerboseAsync(logger); + + // The Packages page indexed the staged folder as an unknown package; re-index now + // that it's gone so the stale card doesn't linger during the install + EventManager.Instance.OnInstalledPackagesChanged(); + + showProgressDialogOnStart = true; + } + else + { + OnPrimaryButtonClick(); + + if (installPath.Exists) + { + await installPath.DeleteVerboseAsync(logger); + } + } + var recommendedPython = selectedPackage.RecommendedPythonVersion; var steps = new List @@ -132,13 +173,6 @@ private async Task InstallPackage(BasePackage selectedPackage) new SetupPrerequisitesStep(prerequisiteHelper, selectedPackage, recommendedPython), }; - // get latest version & download & install - if (Directory.Exists(installLocation)) - { - var installPath = new DirectoryPath(installLocation); - await installPath.DeleteVerboseAsync(logger); - } - var downloadVersion = await selectedPackage.GetLatestVersion(); var installedVersion = new InstalledPackageVersion { IsPrerelease = false }; @@ -208,7 +242,7 @@ private async Task InstallPackage(BasePackage selectedPackage) var runner = new PackageModificationRunner { - ShowDialogOnStart = false, + ShowDialogOnStart = showProgressDialogOnStart, HideCloseButton = false, ModificationCompleteMessage = $"{selectedPackage.DisplayName} installed successfully", }; diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index 55e092056..b8be9d5af 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -1143,8 +1143,7 @@ partial void OnIsSharedModelSymlinkChanged(bool oldValue, bool newValue) } else { - var basePackage = packageFactory[Package!.PackageName!]; - basePackage!.RemoveModelFolderLinks(Package.FullPath!, SharedFolderMethod.Symlink); + RemoveModelFolderLinks(SharedFolderMethod.Symlink); } } @@ -1166,11 +1165,31 @@ partial void OnIsSharedModelConfigChanged(bool oldValue, bool newValue) } else { - var basePackage = packageFactory[Package!.PackageName!]; - basePackage!.RemoveModelFolderLinks(Package.FullPath!, SharedFolderMethod.Configuration); + RemoveModelFolderLinks(SharedFolderMethod.Configuration); } } + private void RemoveModelFolderLinks(SharedFolderMethod sharedFolderMethod) + { + var basePackage = packageFactory[Package!.PackageName!]; + basePackage! + .RemoveModelFolderLinks(Package.FullPath!, sharedFolderMethod) + .SafeFireAndForget(ex => + { + logger.LogError( + ex, + "Failed to remove {Method} model folder links for {Package}", + sharedFolderMethod, + Package.DisplayName + ); + notificationService.Show( + "Failed to remove shared model links", + ex.Message, + NotificationType.Error + ); + }); + } + partial void OnIsSharedModelDisabledChanged(bool value) { if (value) diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs index 890651013..03af4f190 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs @@ -231,9 +231,15 @@ x.PackageName is nameof(ComfyUI) or "ComfyUI-Zluda" InstallName = InstallName.Trim(); var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName); - if (Directory.Exists(installLocation)) + var installPath = new DirectoryPath(installLocation); + + // Require explicit confirmation before deleting an existing non-empty folder at the + // install location — it may be a manually placed installation the user meant to Import + if (!await DialogHelper.ConfirmDeleteExistingInstallDataAsync(installPath)) + return; + + if (installPath.Exists) { - var installPath = new DirectoryPath(installLocation); await installPath.DeleteVerboseAsync(logger); } diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/ConfirmDeleteDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/ConfirmDeleteDialog.axaml index 4040720a7..e6b9c54b6 100644 --- a/StabilityMatrix.Avalonia/Views/Dialogs/ConfirmDeleteDialog.axaml +++ b/StabilityMatrix.Avalonia/Views/Dialogs/ConfirmDeleteDialog.axaml @@ -17,10 +17,8 @@ d:DesignWidth="500" x:DataType="dialogs:ConfirmDeleteDialogViewModel" mc:Ignorable="d"> - - + + + + + +