Ants Projects Hub
← Perch

Changelog

Perch

Every release, newest first — 2 in total.

v1.1.0

1.1.0 — onboarding, a single-file download, and a Flatpak that works

Added

  • First-run setup wizard, opening on the zero-config message (PERC-0003) src/perch/ui/onboarding.py adds the three-page QWizard specified in docs/08-ui.md. Page 1 states the only thing that matters — you don't need to configure anything, just move your windows and Perch remembers (that page is PERC-0004). Page 2 checks the setup: tray visibility, start-at-login, and which compositor was detected. Page 3 offers an optional route into the config dialog. Badge logic for the two real checks lives in pure check_*() functions, so it is tested without a live desktop; start-at-login is a preference and renders on/off, never a warning.

    Gated on a new [general] onboarding_completed, which defaults false and is absent from the seeded config, so a fresh install and an upgrading config both see the wizard exactly once. It is written on every exit — Finish, Cancel and window-close — so it never reappears unprompted; Finish also persists and applies the autostart choice, while Cancel changes no system setting. Settings → General carries a Run setup wizard again… button, which saves the dialog first and re-seeds the page afterwards so the two cannot fight over Start at login.

    The GNOME-Wayland AppIndicator guidance is now shared between the wizard's tray row and the standalone hint in perch.app, so the two cannot drift.

  • Tray menu: a Donate submenu and a Report an issue entry (PERC-0035) Donate opens as a submenu with one entry per destination in .github/FUNDING.yml; Report an issue opens the GitHub issue tracker. Both go through QDesktopServices.openUrl, which resolves to the OpenURI portal under Flatpak and needs no extra sandbox permission. The destinations are stated in perch.ui.links because .github/ is not shipped in any package, and a test asserts they match FUNDING.yml.

  • README install section for the openSUSE and Fedora RPM repository (PERC-0041) home:milnet:perch on OBS builds green for openSUSE Tumbleweed and Fedora and is now advertised, with copy-paste zypper and dnf commands. Both repositories were checked to be serving the RPM before the link went in.

  • local_CI.sh --docs — a documentation-only push no longer runs the full suite (PERC-0034) New tools/docs_check.py verifies every relative link in the docs set resolves and that no retired or forbidden string has crept outside the documents that record it. It runs as a third ci.yml job and, via --docs, alone — the pre-push hook selects that for a documentation-only push, turning roughly half a minute of gate into well under a second. docs/contributing-dev-setup.md owns what counts as documentation.

  • Self-contained AppImage — a single-file, zero-dependency Linux download Download → chmod +x → run; no Python, PySide6, or system packages for the user to install. Bundles the interpreter, Qt, and the xcb platform libraries (verified self-contained on a bare container). Recipe at packaging/appimage/; built and attached to releases by .github/workflows/release.yml.

Changed

  • Dropped --filesystem=xdg-config/perch:create from the Flatpak manifest (PERC-0036) It was justified as sharing one config file with a native install, which it never did: the sandbox redirects XDG_CONFIG_HOME, so Perch's config stays inside the sandbox and the host grant went unused. Flatpak Perch keeps its own config, which is the Flathub norm.

  • OBS submission targets the home:milnet:perch subproject and uploads a release tarball (PERC-0041) The subproject matches the convention already used for this account's other projects, and gives Perch its own repository list (Tumbleweed and Fedora, both x86_64). packaging/rpm/_service is deleted: its obs_scm entry was mode="manual", so OBS never ran it and the build died with no .obsinfo file found — and having any buildtime service pulled the obs-service-* packages into the build root, where Fedora could not resolve wget. Two targets failing for two unrelated reasons, from one mechanism nothing needed. packaging/submit/obs.sh now uploads the GitHub release tarball, which is all Source0 ever wanted; it also looked for ~/.oscrc when osc uses ~/.config/osc/oscrc, so it refused to run on a correctly configured machine.

  • Live and planned work moved to ROADMAP.md at the repo root; docs/11-roadmap.md is now history ROADMAP.md is a generated render of the roadmap store, and each item carries a PERC-NNNN id. docs/11-roadmap.md keeps its number and filename (the numbered sequence is the reading order and is stable, per docs/filename-standards.md) but is retitled "Roadmap history" and now says up front what it is: the record of how Perch reached v1.0.0 — the phased plan, each milestone's exit criteria and the evidence they were met, the ground rules, the known risks, and the Phase 2 / 2.5 research logs. It had still described itself as the source of truth for milestone ordering.

  • Tray "Pause restore" is now "Pause Perch" — a full panic switch Paused now suppresses every automatic placement (rules, layouts, and last-seen restore), not just the last-seen auto-restore, so no window is moved while paused. Manual "Snap focused" still applies. Renamed the TogglePauseRestore intent to TogglePause. See docs/08-ui.md §Menu structure.

Removed

  • Fedora COPR dropped as a distribution channel — OBS builds the Fedora RPM from the same spec (PERC-0041) It would have been a second build service producing one artefact from one spec, with a second set of credentials and a second thing to keep current. Fedora users are unaffected: OBS builds Fedora targets, and the %if 0%{?fedora} guards in the spec stay, because package names differ between the distro families regardless of who builds them. packaging/submit/copr.sh is deleted. The spec also cited a packaging/rpm/COPR.md that never existed.

Fixed

  • X11 backend: a display that goes away during start() no longer crashes Perch An X server can accept the connection and then reset it mid-handshake — a session ending as Perch starts, or a freshly launched Xvfb. python-xlib wraps that in Xlib.error.ConnectionClosedError, which subclasses Exception alone and so escaped start()'s OSError / ConnectionError handler as a raw traceback. It is now named at all three points it can fire (the Display() constructor, the subscription handshake, the first cache-priming round-trips) and raises the documented BackendUnavailable, tearing the half-open display down first, so the core takes its UI-only fallback path instead. Caught by CI run 33145715623.

  • Flatpak autostart: read the portal's Response, not the Request path (PERC-0037) org.freedesktop.portal.Background.RequestBackground returns the object path of an org.freedesktop.portal.Request; the outcome arrives later as that request's Response signal. portal_set_autostart read the returned path as the result dict, so every Flatpak autostart toggle raised AttributeError: 'str' object has no attribute 'get' in a task nobody retrieved — autostart silently never took effect and nothing was logged. It now correlates the Response the way PortalGlobalShortcutsProvider already did, returns whether autostart was granted, and logs a warning on a refusal, a non-zero response code or a timeout.

  • The tray-host probe no longer reports "no host" inside a Flatpak (PERC-0040) It let sdbus pick the default bus, which fails outright in a sandbox where DBUS_SESSION_BUS_ADDRESS is /run/flatpak/bus; every failure is classified as "no host", so the wrong answer was silent. On GNOME Wayland that fired the "install the AppIndicator extension" dialog at users who did not need it.

  • Tray icons no longer come out null on an installed layout (PERC-0039) The bundled-SVG fallback resolved a path relative to the source tree, which only exists in a dev checkout — under Flatpak it pointed at a directory that is not there, and the icon-theme lookup does not cover for it. The XDG data directories are now searched first, which name /app inside a Flatpak and /usr under an RPM; sys.prefix does not, because a Flatpak's interpreter comes from the runtime.

  • Flatpak tray icon now appears — granted the StatusNotifierWatcher talk-name (PERC-0038) The sandbox answered ServiceUnknown for org.kde.StatusNotifierWatcher, so Perch could neither probe for a StatusNotifier host nor register its item. Perch has no window, so this left the Flatpak with no interface at all.

  • KWin script now installs to the host path under Flatpak (PERC-0036) Inside a Flatpak, XDG_DATA_HOME is redirected into the sandbox, so the bundled KWin script was mirrored to a path KWin — which runs on the host — cannot read, leaving the KWin backend unable to drive anything. The target is now resolved from $HOME when sandboxed. PERCH_KWIN_SCRIPT_TARGET still overrides everything.

  • The RPM spec now builds on Fedora — five bugs no local check could catch (PERC-0041) Publishing to OBS for the first time found them all. rpmspec -P passed throughout, because every one of them only fails inside a real build root: openSUSE-only BuildRequires names (appstream-glib, libxml2-tools) left Fedora unresolvable; a %build comment naming %pyproject_wheel unescaped made rpm call the macro with the sentence as arguments; %{_metainfodir} is Fedora-only and undefined on openSUSE; the build selected a python flavor the build root did not ship; and the icon directories were unowned. Fedora now produces perch-1.0.0-7.1.noarch.rpm.

  • The Flathub manifest now builds — it was based on the wrong Qt toolkit and could not be built by anyone (PERC-0042) It based on com.riverbankcomputing.PyQt.BaseApp, the PyQt base app, for an application built on PySide6; it targeted a KDE runtime eight months stale; and its Python dependency includes were commented out and deliberately not committed, to be generated at submission time — so a fresh clone could not build it at all. It now uses io.qt.PySide.BaseApp//6.11 on org.kde.Platform//6.11, with the dependency closure sha256-pinned and committed as python3-deps.yaml, which is what Flathub's network-free builders require. Verified by a real offline build of the submission manifest.

    New alongside it: generate-pip-sources.sh regenerates the closure from pyproject.toml, flatpak-build.sh reproduces Flathub's build locally and smoke-tests the result, and flathub.json restricts the buildbot to the arch the pinned wheels cover. packaging/submit/flathub.sh no longer does the work itself; it also no longer targets the wrong base branch — a new app is PR'd against new-pr, not master.

  • Docs no longer promise an "Include last-seen geometries" export checkbox that does not exist docs/02-state-format.md described an export checkbox for shipping state.json alongside the config, contradicting its own Export bullet and docs/08-ui.md. Export copies config.toml verbatim and has never included state.json. The section now also states a round-trip criterion: what must survive an export/import, what need not, and what travels but stays inert until the target machine has matching monitors. (PERC-0031, PERC-0032)

  • "Reapply rules now" is no longer a silent no-op The tray "Reapply rules now" action was wired to recompute_topology(), whose topology-key early-return meant no window was re-evaluated unless the monitor layout had changed. It now calls a dedicated Reducer.reapply() that re-evaluates every open window regardless of topology, matching the ReapplyRules intent's contract.

  • Tray snap-preset labels are now translatable The built-in snap-preset labels (Left half, Right half, …) were marked for extraction with a context-less QT_TR_NOOP but translated at runtime under the perch.ui.tray context, so a translator's work never reached the tray menu. They now use QT_TRANSLATE_NOOP("perch.ui.tray", …) and translations/perch_en.ts was regenerated (also picking up strings added since M3 that had never been re-extracted).

v1.0.0

Perch v1.0.0

First stable release of Perch — a persistent, compositor-aware window geometry manager for Linux desktops.

Sits in the system tray, remembers where each window belongs, and restores position / size / monitor / virtual desktop when a window reopens. Ships a rules engine, named layouts, per-monitor profiles, snap presets, and global hotkeys.

Highlights

  • Tray + PySide6 config dialog (General / Windows / Rules / Layouts / Profiles / Hotkeys / Exclusions / Import-Export) with dark-theme support, keyboard navigation, and accessible labels.
  • Windows pane — live table of tracked windows with per-row Save-as-last-seen / Forget-last-seen / Apply-preset buttons.
  • Tray "Snap focused window" submenu — maximise, half-halves, quarters, Centre (keep size), Centre (60 %).
  • Rules engine — match by app_id / wm_class / title (regex) / PID / type, apply geometry / snap / monitor / desktop / maximized. 100 % line-covered evaluator.
  • Full backends for X11 (any EWMH WM) and KWin / Plasma Wayland (bundled JS script + long-poll IPC). Community stubs for Mutter, Sway, Hyprland.
  • Global hotkeys via the xdg-desktop-portal GlobalShortcuts interface, with KGlobalAccel and XGrabKey as platform fallbacks.
  • Autostart via XDG .desktop files (non-Flatpak) or the Background portal (Flatpak).
  • Packaging for Flathub, openSUSE OBS, Fedora COPR, AUR (perch stable + perch-git), and the KDE Store.

What's new since the design-complete snapshot

Everything. See CHANGELOG.md for the full per-milestone history, including the post-smoke-test fixes (M9.f.1…M9.f.15) that wired every dialog pane to real functionality, fixed the KWin script's Qt is not defined bug, and shipped the intent-dispatch audit that gates CI against future shipped-stub regressions.

Install

Packaging artefacts are authored in packaging/ — Flathub, OBS, COPR, AUR, KDE Store. Flathub submission, OBS project, and AUR push follow from this tag.

From source:

git clone https://github.com/milnet01/perch.git
cd perch && git checkout v1.0.0
pip install --user --break-system-packages -e ".[dev]"
perch

Requires Python 3.12+ and PySide6 ≥ 6.8.

License

GPL-3.0-or-later.