github fuddlesworth/PlasmaZones v3.1.0
PlasmaZones v3.1.0

5 hours ago

PlasmaZones v3.1.0

Added

  • Window Rules: a new unified settings page replaces the old Snapping Assignments, Tiling Assignments, Animations App Rules, and per-mode "disabled apps" lists. Rules are browsed, added, edited, drag-reordered, duplicated, and disabled from one place. Matching composes class, title, role, app-id, virtual desktop, activity, and screen predicates with AND/OR/NOT. A rule's actions cover snapping/tiling assignment, animation-curve and shader overrides, and exclusion from snapping, autotile, and effects, the four surfaces that previously each had their own editor.
  • org.plasmazones.WindowRules D-Bus interface (dbus/org.plasmazones.WindowRules.xml): getAllRules, setAllRules, addRule, removeRule, and related lifecycle methods for programmatic rule management.
  • phosphor-window-rules LGPL-2.1+ library housing the rule model, parser, and RuleEvaluator, so third parties can link the matcher without inheriting GPL.
  • Snapping focus behavior: two new opt-in toggles on Snapping → Window → Behavior (both default off). focus new windows auto-activates a window when it is auto-placed into a zone on open, and focus follows mouse activates the snapped window under the cursor. Brings snapping to parity with the existing autotile focus options.
  • Zone span toggle mode (#563): an opt-in switch in the Zone Span card so the span modifier can be tapped to start/stop spanning instead of held down for the whole drag (default off, motivated by accessibility).
  • Restore floated window positions on login (#606): floated windows are now restored to the monitor and position they closed on after a KWin session restore (previously only snapped windows were restored cross-screen). Parallel per-engine toggles (both default on), restore unsnapped windows under Snapping → Window → Behavior and restore untiled windows under Tiling → Behavior, plus an engine-neutral per-window RestorePosition rule action let specific windows opt in or out for either mode.
  • Per-window appearance for snapping: snapping gains its own border, corner-radius, hide-title-bar, and accent-color settings, mirroring tiling, and the former "Snapping → Appearance" page is renamed Zones. Window restore state across daemon restart and logout/login is now backed by a single WindowPlacementStore instead of several overlapping mechanisms.
  • New window-rule actions for window chrome: per-window border, title-bar, corner-radius, accent-color, gap/padding, and opacity overrides, applied to snapped or floating windows (e.g. "floating windows on monitor 2 → no title bar + red border", "activity Gaming → zero gaps").
  • New window-rule match conditions: IsTransient, IsNotification, Width, Height, and IsFocused, plus a built-in "Don't animate small windows" template. IsFocused lets any action be focus-scoped (e.g. "WHEN NOT focused → dim").
  • Collapsible settings sidebar categories with smart-expand of the active page's category and animated chevrons, cutting clicks to reach deep pages.
  • Per-monitor scope map: per-monitor settings cards carry a scope chip that opens a spatial map of the real monitor arrangement to switch outputs, replacing the tall repeated monitor-selector block. The scope choice persists across pages.
  • Shader-driven window-move/resize morph (window-morph): window move, resize, snap, and layout-switch transitions animate as a smooth shader cross-fade geometry morph instead of a plain C++ paint transform, the default for window-move and overridable per event. Overlay show/hide (OSD, zone selector, layout picker, snap assist) now defaults to the shader-based fade effect.
  • In-app live shader preview: the Snapping → Shaders browser gains an animated, interactive preview with mouse and audio input, shader presets (load/save, shared with the editor), and an in-app compile-error banner.
  • Shader authoring API: authors write only the effect body and read parameters by name (p_<id>) instead of decoding UBO slots, with a generated preamble and entry-point conventions, plus a plasmazones-shader-validate CLI (with --animation / --overlay modes and did-you-mean diagnostics) wired into CI to catch broken packs offline.
  • dma-buf zero-copy window-preview transport for snap-assist thumbnails (opt-in via the PLASMAZONES_DMABUF_THUMBNAILS env var, with default builds unchanged), the foundation for live window previews.
  • Phosphor SDK groundwork: the reusable LGPL Phosphor library line gains a Phase 1 foundation tier (phosphor-theme, phosphor-popout, phosphor-registry, phosphor-ipc + the phosphorctl driver, phosphor-shell per-screen helper) and a Phase 2 system-service tier (phosphor-service-{pipewire,network,bluetooth,brightness,notifications,polkit,idle,clipboard,lock,session,upower,mpris,icontheme,sni}). All of it is gated behind BUILD_PHOSPHOR_SHELL (default off) and driveable only from standalone examples/CLIs. It is groundwork for the standalone Phosphor shell direction and is not part of the shipping PlasmaZones tiler.
  • Resize-aware tiling (#666): six split-ratio algorithms (master-stack, wide, focus-sidebar, zen, deck, and horizontal-deck) now reflow on interactive resize. Dragging the master or boundary edge updates the split ratio for that desktop the same way a master-ratio keystroke does, without bleeding into other screens or the global default. The Layouts page shows a per-algorithm Reflows badge and can group algorithms by it.
  • Suppress default layout assignment (#676): a new setting, with a matching DefaultLayoutAssignment window-rule action, stops a context from falling back to the synthesized default layout. A suppressed context shows no snapping overlay or zone selector, reports no layout, and shows a "No layout assigned" OSD. Switching it into autotile sets the mode without applying the global default algorithm until a concrete one is assigned.
  • Layouts page rebuilt as a searchable, card-based catalogue with collapsible capability groups, per-layout deep links, and global-search reveal, matching the shader browser and Window Rules. Tiling algorithms expose a Script State capability (filter, group-by, and a card badge) alongside the reflow and persistent-memory badges, layout and algorithm cards show their description on hover, and bundled snapping layouts open in a text editor.

Changed

  • Single rule format: window assignments, per-mode disable lists, animation App Rules, and effect exclusion lists are unified into one rule list stored in ~/.config/plasmazones/windowrules.json. The KWin effect now consults the same RuleEvaluator as the daemon for animation App-Rule resolution and exclusion checks, so the two cannot drift.
  • LayoutRegistry::walkCascade removed, replaced by RuleEvaluator. The old per-axis cascade (context-keyed assignments vs window-property matching) no longer exists. All matching goes through the evaluator.
  • org.plasmazones.WindowTracking.setWindowMetadata widened from 4 to 9 arguments to carry the additional fields the evaluator needs (role, app-id, desktop, activity, screen). The KWin effect and daemon must be installed and running as a matched pair. MinPeerApiVersion bumped 3 → 4, and either side refuses to register a mismatched peer rather than silently degrading. Packagers must rebuild and ship both binaries together.
  • org.plasmazones.Layout.assignmentChangesApplied signal dropped its second argument (the per-key field tag). Subscribers that depended on that field must update or they will receive the wrong arity.
  • Scripted autotiling moved from QJSEngine (JavaScript) to an embedded, sandboxed Luau VM (phosphor-scripting). The 25 bundled algorithms were ported *.js*.luau, written against a new frozen pz standard library, with a per-engine CPU-time watchdog and a 64 MiB heap cap. The TilingAlgorithm contract, daemon, editor, and settings are unchanged. Breaking for custom algorithms: the loader now discovers only *.luau files, so user scripts in ~/.local/share/plasmazones/algorithms/ written in the old JavaScript form are no longer loaded and must be rewritten in Luau (see docs/architecture/luau-algorithm-authoring.md).
  • Snapping and Tiling settings aligned for parity. Tiling gains a dedicated Focus card. Section and label naming is unified across both modes ("Inner gap" / "Outer gap", "Window Handling", parallel quick-shortcut labels), and the placement settings are reorganized into a consistent Overlay / Window / Configuration shape with gaps moved into Window → Appearance and per-monitor gap selectors. User settings are preserved (C++ symbol renames only).
  • Performance. Daemon peak heap is down ~58% (149 → 63 MB) and idle CPU drops from ~25% to ~0. The overlay and snap-assist release their full-screen image buffers when dismissed (≈33 MB per shader-enabled 4K screen, ≈6 MB of thumbnail cache), and a content-addressed on-disk shader cache speeds warm launches. Settings pages that were slow on first visit now open fast via a page-instance cache, background compile-warming, and viewport virtualization of the animation-event card lists.
  • Settings app rebuilt on the reusable phosphor-control library (extracted from the in-app settings chrome, formerly named phosphor-settings-ui), reducing the app to a thin consumer with visual parity.
  • Internal pzp symbol-prefix debrand across shader params, classes, macros, CMake helpers, and the Luau tiling global (pzphosphor_luau). The user-facing PlasmaZones brand and the global-shortcut ID namespace are deliberately unchanged. All ad-hoc registries (shaders, animation, curves, tiles algorithms, layout sources) are unified onto a single thread-safe Registry<T> primitive with public APIs preserved.
  • Nix flake restructured around a single package definition. The 312-line flake.nix is now thin wiring, and the build recipe and the KWin-IID rationale live in packaging/nix/{package,overlays,module,hm-module,devShell,formatter}.nix. The package is defined once in overlays.nix (final.callPackage) and every output (packages, devShells, checks, formatter) derives from legacyPackages.<system>.extend overlay, replacing five independent build call sites that each had to remember to build against the right pkgs. The version is parsed once from the top-level project(PlasmaZones VERSION …) in CMakeLists.txt inside flake.nix (where the flake self is a store path, so the read is pure) and threaded to the package as an argument. Reading it inside package.nix forced an import-from-derivation when nixpkgs builds from a fetchFromGitHub src. LTO is now opt-in (enableLTO, default off) instead of forced, since every module/overlay consumer rebuilds against host pkgs with no cache reuse. The build source is lib.fileset-scoped so editing docs/CI/flake files no longer invalidates it. nix fmt now formats Nix, C++, and QML (reusing the in-tree .clang-format), and the NixOS module declares the plasmazones systemd user service with autostart opt-in (default off, preserving the per-user "enable it yourself" policy).
  • Autotiling is on by default (#671): tiling now works out of the box so PlasmaZones behaves like a dynamic tiler with no setup. The companion behaviors (focus new windows, smart gaps, respect minimum size, exclude transient windows, and insert at the stack end) were already on by default, and the default algorithm (bsp) and gaps are unchanged. Only fresh installs are affected, and existing saved configs keep their current value.
  • Settings UI polish across the Layouts and listing pages: a curated default picker shows a starter set of layouts and algorithms with the rest one eye-toggle away, a shared filter menu now drives the Layouts, Window Rules, and Shaders lists, and the sidebar, global search field, About credits, and virtual-screen preview labels got alignment and spacing fixes.

Removed

  • Legacy Display.SnappingDisabled* and Display.AutotileDisabled* config keys (auto-migrated into rules).
  • QJSEngine-based scripted-tiling path (the ScriptedAlgorithm runtime, its JS builtins, and the bundled *.js algorithms), along with the Qt6::Qml dependency in phosphor-tiles. Replaced by the Luau path above.
  • setSnappingLayoutEntry, setTilingAlgorithmEntry, and related per-field Settings-side Q_INVOKABLEs that the legacy KCM Assignments pages used. There is no QML replacement. Use the Window Rules page.
  • Legacy Snapping Assignments, Tiling Assignments, and Animations App Rules settings pages (replaced by Window Rules).
  • Per-release plasmazones.nix asset and its generate-release-nix.sh generator. The asset was a source-pinned build recipe (not a binary), so it saved no build time and only served non-flake Nix users, who can instead build any tag against their host's pkgs with pkgs.callPackage "${builtins.fetchTarball "https://github.com/fuddlesworth/PlasmaZones/archive/v<VERSION>.tar.gz"}/packaging/nix/package.nix" { version = "<VERSION>"; }. The release notes' standalone-install section now shows that form, and the build-nix release job is reduced to a nix build smoke gate. Flake users are unaffected.
  • Stray develop.nix: an unrelated dev flake (for "canaanepperson.com", nodejs_24) that had no connection to PlasmaZones. The dev environment lives in the flake's devShells.default.

Migration

  • Config schema bumped v3 → v4. On first launch after upgrade, ~/.config/plasmazones/assignments.json is automatically converted into ~/.config/plasmazones/windowrules.json, and the legacy Display.SnappingDisabled* / Display.AutotileDisabled* keys in config.json are folded into the same rule set. The migration is lossless and runs without user interaction.
  • Backout: the source file is renamed assignments.json.migrated (not deleted), so a downgrade can restore the previous schema by manually renaming it back and starting an older daemon.
  • Recovery: if migration aborts because the source is malformed, the original file is renamed to ~/.config/plasmazones/assignments.json.corrupt.bak, the schema version stays at v3, and windowrules.json is not created. The daemon does not silently flush the old assignments to an empty rule set. The user can inspect / repair the quarantined file and rename it back to assignments.json, and the next launch then retries the v3→v4 conversion.
  • hiddenFromSelector now relocates out of layout files during the v3→v4 layout-settings conversion. A v3 user who hid a layout previously kept the key embedded in the slimmed layout file instead of having it moved to the layout-settings.json sidecar. The migration now carries it across with the other relocated keys. Autotile per-algorithm overrides also fold into layout-settings.json, and the standalone autotile-overrides.json is retired by a one-time self-deleting migration on load.

Fixed

  • Layouts rendered stretched / ultrawide when editing on a 16:9 or 4K screen (#593): the editor canvas used a fixed-zone bounding box as its aspect reference, so editing an existing layout could distort it while creating a new one rendered correctly. The canvas now references the live screen unless fixed zones genuinely overflow it.
  • A zone-spanning window blew up to fullscreen when switching layouts (#575): switching to a layout where the previously-spanned zones are non-contiguous (e.g. Grid 2×2 left column → Master+Stack) unioned them into a screen-sized bounding box. Non-contiguous mapped spans now collapse to the primary zone instead.
  • KWin effect plugin silently never installed under Nix. packaging/nix/package.nix set KDE_INSTALL_QTPLUGINDIR to an absolute path inside the read-only qtbase store output (${qt6.qtbase}/lib/qt6/plugins). A derivation may only write under its own $out, so the effect dropped out of the package closure entirely. The daemon ran but zone overlays never appeared on Nix installs. The plugin now installs into the package's own $out/${qt6.qtbase.qtPluginPrefix} (the canonical NixOS lib/qt-6/plugins layout), where the running KWin discovers it via the system profile's aggregated QT_PLUGIN_PATH.
  • Toggling from autotile back to snapping left windows stuck in their tiled positions instead of returning to where they were before tiling. The transition fell through to a stale current-assignment resnap that re-pinned the tiled geometry and suppressed the float-back. Windows now float back to their pre-tile positions.
  • Master-ratio and master-count adjustments bled into the global default (#666): the increase/decrease ratio and master-count shortcuts wrote the new value into the global config whenever a screen had no per-screen override, so the tweak propagated to sibling screens and new states on the next algorithm switch or settings refresh. The adjustment now stays local to the active screen, desktop, and activity, and resets only on an algorithm switch or an explicit ratio/count change in settings.

Installation

Arch Linux (AUR):

yay -S plasmazones  # or plasmazones-bin

Arch Linux (manual):

sudo pacman -U plasmazones-3.1.0-*-x86_64.pkg.tar.zst

KDE Neon / Debian-based:

sudo dpkg -i plasmazones_3.1.0-*_amd64.deb
sudo apt-get install -f  # Install dependencies if needed

Fedora (COPR):

sudo dnf copr enable fuddlesworth/PlasmaZones
sudo dnf install plasmazones

Fedora (manual RPM):

# Fedora 44
sudo dnf install plasmazones-3.1.0-*.fc44.x86_64.rpm

openSUSE Tumbleweed (OBS):

sudo zypper addrepo https://download.opensuse.org/repositories/home:fuddlesworth/openSUSE_Tumbleweed/home:fuddlesworth.repo
sudo zypper refresh
sudo zypper install plasmazones

Universal Linux (AppDir):
For Fedora Atomic, Steam Deck, or non-root user installation:

tar xzf plasmazones-3.1.0-linux-x86_64.tar.gz
cd plasmazones-linux-x86_64
./install.sh

NixOS (flake):

# flake.nix inputs
plasmazones.url = "github:fuddlesworth/PlasmaZones";

# configuration.nix
programs.plasmazones.enable = true;

NixOS (without flakes):
Build this tag's source against your host's pkgs (no release asset needed):

# configuration.nix
environment.systemPackages = [
  (pkgs.callPackage
    "${builtins.fetchTarball "https://github.com/fuddlesworth/PlasmaZones/archive/v3.1.0.tar.gz"}/packaging/nix/package.nix"
    { version = "3.1.0"; })
];

Post-Installation

systemctl --user enable --now plasmazones.service
systemsettings kcm_plasmazones

Don't miss a new PlasmaZones release

NewReleases is sending notifications on new releases.