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.pyadds the three-pageQWizardspecified indocs/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 purecheck_*()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 overStart 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 throughQDesktopServices.openUrl, which resolves to the OpenURI portal under Flatpak and needs no extra sandbox permission. The destinations are stated inperch.ui.linksbecause.github/is not shipped in any package, and a test asserts they matchFUNDING.yml.README install section for the openSUSE and Fedora RPM repository (PERC-0041)
home:milnet:perchon 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) Newtools/docs_check.pyverifies 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 thirdci.ymljob 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.mdowns 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:createfrom the Flatpak manifest (PERC-0036) It was justified as sharing one config file with a native install, which it never did: the sandbox redirectsXDG_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:perchsubproject 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/_serviceis deleted: itsobs_scmentry wasmode="manual", so OBS never ran it and the build died withno .obsinfo file found— and having any buildtime service pulled theobs-service-*packages into the build root, where Fedora could not resolvewget. Two targets failing for two unrelated reasons, from one mechanism nothing needed.packaging/submit/obs.shnow uploads the GitHub release tarball, which is allSource0ever wanted; it also looked for~/.oscrcwhen 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.shis deleted. The spec also cited apackaging/rpm/COPR.mdthat 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 launchedXvfb. python-xlib wraps that inXlib.error.ConnectionClosedError, which subclassesExceptionalone and so escapedstart()'sOSError/ConnectionErrorhandler as a raw traceback. It is now named at all three points it can fire (theDisplay()constructor, the subscription handshake, the first cache-priming round-trips) and raises the documentedBackendUnavailable, 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.RequestBackgroundreturns the object path of anorg.freedesktop.portal.Request; the outcome arrives later as that request'sResponsesignal.portal_set_autostartread the returned path as the result dict, so every Flatpak autostart toggle raisedAttributeError: '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 wayPortalGlobalShortcutsProvideralready 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_ADDRESSis/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
/appinside a Flatpak and/usrunder an RPM;sys.prefixdoes 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
ServiceUnknownfororg.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_HOMEis 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$HOMEwhen sandboxed.PERCH_KWIN_SCRIPT_TARGETstill 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 -Ppassed throughout, because every one of them only fails inside a real build root: openSUSE-onlyBuildRequiresnames (appstream-glib,libxml2-tools) left Fedora unresolvable; a%buildcomment naming%pyproject_wheelunescaped 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 producesperch-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 usesio.qt.PySide.BaseApp//6.11onorg.kde.Platform//6.11, with the dependency closure sha256-pinned and committed aspython3-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.shregenerates the closure frompyproject.toml,flatpak-build.shreproduces Flathub's build locally and smoke-tests the result, andflathub.jsonrestricts the buildbot to the arch the pinned wheels cover.packaging/submit/flathub.shno longer does the work itself; it also no longer targets the wrong base branch — a new app is PR'd againstnew-pr, notmaster.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).