diff --git a/config/dot/hypr/rules.lua b/config/dot/hypr/rules.lua index bf77de0..d934ed1 100644 --- a/config/dot/hypr/rules.lua +++ b/config/dot/hypr/rules.lua @@ -77,6 +77,18 @@ hl.window_rule({ center = true, }) +-- Quick Look. The GNOME previewer is what the file manager opens on space, +-- and it is an overlay rather than a window someone manages: tiled, it shoves +-- the file manager aside and has to be dismissed before the list is usable +-- again. Sized generously because a preview that needs zooming is not a +-- preview; it still gets a margin so the file underneath stays visible. +hl.window_rule({ + match = { class = "^org\\.gnome\\.NautilusPreviewer$" }, + float = true, + size = { "monitor_w * 0.7", "monitor_h * 0.8" }, + center = true, +}) + -- Portal dialogs (file chooser, screen share picker) should always float. hl.window_rule({ match = { class = "^(xdg-desktop-portal-gtk|org\\.freedesktop\\.impl\\.portal\\.desktop\\.gtk|hyprland-share-picker)$" }, diff --git a/docs/superpowers/plans/2026-08-19-desktop-integration.md b/docs/superpowers/plans/2026-08-19-desktop-integration.md index 2784c09..7600b20 100644 --- a/docs/superpowers/plans/2026-08-19-desktop-integration.md +++ b/docs/superpowers/plans/2026-08-19-desktop-integration.md @@ -33,15 +33,23 @@ install seeds `org.gnome.Loupe`, `org.gnome.Papers` and `org.gnome.Decibels` only when they exist; otherwise it correctly leaves the role alone and the machine falls back to installation order — the exact problem we just fixed. -- Declare Loupe, Papers, Decibels and sushi in the dependency list so - `declared-dependencies-contract` fails when a curated handler is missing. -- Wire preview: confirm space-to-preview from the file manager, and decide - whether preview is worth a compositor-level keybind for the focused selection. -- Extend the family contract to assert every seeded candidate is installable. +- Declare Loupe, Papers, Decibels, Nautilus, mpv and sushi so a fresh machine + actually has what seeding prefers. +- Wire preview. +- Extend the family contract to assert every preferred handler is a declared + package. **Done when** a fresh machine gets the same handlers this one has, and the contract fails loudly if a curated application is not a declared dependency. +**Landed 2026-08-19.** The previewer answers on `org.gnome.NautilusPreviewer2`, +not the `NautilusPreviewer` interface its bus name suggests, and it opened +*tiled* -- shoving the file manager aside for something meant to be an overlay +-- so it now has a float-and-center window rule. No compositor keybind: the +selection to preview lives in the file manager, and nothing outside it knows +what is selected. `xdg-utils` and `desktop-file-utils` were also undeclared, +which the dependency contract caught the moment seeding started calling them. + ## Phase 2 — Make GTK applications wear our accent (small) The named accents theme our own surfaces; Loupe, Papers, Nautilus and every other diff --git a/setup/packages/desktop-packages b/setup/packages/desktop-packages index 564aa2b..f3b709c 100644 --- a/setup/packages/desktop-packages +++ b/setup/packages/desktop-packages @@ -1,6 +1,8 @@ adw-gtk3-theme akmods alsa-plugins-pulseaudio +decibels +desktop-file-utils dnf-plugins-core ffmpeg fuse-libs @@ -25,19 +27,24 @@ lame-devel libva-devel libva-utils libxcrypt-compat +loupe mesa-libGL-devel mesa-libGLU mesa-libOpenCL mokutil mozilla-openh264 +nautilus nautilus-extensions nautilus-python nextcloud-client openssl-devel opus-devel +papers python3-dnf-plugin-versionlock rocm-opencl +sushi wine wine-core wine-mono winetricks +xdg-utils diff --git a/setup/packages/flatpak-packages b/setup/packages/flatpak-packages index 9e26c1a..a6e2e21 100644 --- a/setup/packages/flatpak-packages +++ b/setup/packages/flatpak-packages @@ -4,3 +4,4 @@ com.bitwarden.desktop app.bluebubbles.BlueBubbles org.mozilla.thunderbird_esr io.missioncenter.MissionCenter +io.mpv.Mpv diff --git a/tests/quickshell/declared-dependencies-contract.sh b/tests/quickshell/declared-dependencies-contract.sh index 6b901f5..da35851 100755 --- a/tests/quickshell/declared-dependencies-contract.sh +++ b/tests/quickshell/declared-dependencies-contract.sh @@ -56,6 +56,8 @@ package_for() { notify-send) printf 'libnotify' ;; wl-copy|wl-paste) printf 'wl-clipboard' ;; rg) printf 'ripgrep' ;; + xdg-mime|xdg-settings|xdg-open) printf 'xdg-utils' ;; + update-desktop-database|desktop-file-validate) printf 'desktop-file-utils' ;; python3) printf 'python3' ;; *) printf '%s' "$1" ;; esac diff --git a/tests/quickshell/default-apps-family-contract.sh b/tests/quickshell/default-apps-family-contract.sh index 0ac657c..fe6c0de 100755 --- a/tests/quickshell/default-apps-family-contract.sh +++ b/tests/quickshell/default-apps-family-contract.sh @@ -92,6 +92,53 @@ extra="$(comm -13 <(printf '%s\n' "$helper_roles") <(printf '%s\n' "$page_roles" [[ -z "${missing// }" ]] || fail "the helper can set these roles but the settings page never offers them: $missing" [[ -z "${extra// }" ]] || fail "the settings page offers roles the helper cannot set: $extra" +# Every application a role is curated to must be installed by Panama's own +# package lists. +# +# Seeding is deliberately conservative: a role whose preferred applications are +# all missing is left alone rather than forced. That is the right behavior and +# it is also silent -- so a curated handler nobody installs presents as the +# machine quietly going back to deciding defaults by installation order, which +# is the whole problem this was built to fix. +# +# The package name for each handler is spelled out here because a desktop id +# does not carry one; rpm and flatpak name the same application differently and +# neither name is derivable from the other. +declare -A HANDLER_PACKAGE=( + [org.gnome.Loupe.desktop]=loupe + [org.gnome.Papers.desktop]=papers + [org.gnome.Decibels.desktop]=decibels + [org.gnome.Nautilus.desktop]=nautilus + [io.mpv.Mpv.desktop]=io.mpv.Mpv +) + +declared="$(cat "$repo_dir"/setup/packages/* 2>/dev/null | sed 's/#.*//' | tr -d ' ' | grep -v '^$' | sort -u)" +[[ -n "$declared" ]] || fail 'no package lists found' + +preferred="$(python3 - "$helper" <<'PREFERRED' +import ast, re, sys +source = open(sys.argv[1]).read() +table = ast.literal_eval(re.search(r"PREFERRED_HANDLERS = (\{.*?\n\})", source, re.S).group(1)) +for role, candidates in table.items(): + # Only the first choice has to be installable: the rest are fallbacks for + # machines that happen to have something else, and demanding all of them be + # declared would mean installing three image viewers. + print(role + "\t" + candidates[0]) +PREFERRED +)" || fail 'could not read PREFERRED_HANDLERS from the helper' + +while IFS=$'\t' read -r role handler; do + [[ -n "$role" ]] || continue + # Entries this repository ships itself are installed by link-dotfiles, not + # by a package manager. + [[ -r "$repo_dir/config/local/share/applications/$handler" ]] && continue + package="${HANDLER_PACKAGE[$handler]:-}" + [[ -n "$package" ]] \ + || fail "the \"$role\" role prefers $handler, which this contract has no package name for -- add it to HANDLER_PACKAGE" + grep -qx "$package" <<<"$declared" \ + || fail "the \"$role\" role prefers $handler but nothing declares \"$package\", so a fresh machine seeds nothing for it" +done <<<"$preferred" + # The editor Panama ships must launch in Panama's terminal. The stock # nvim.desktop sets Terminal=true, which hands the launch to whatever the # system considers default -- not necessarily the terminal this desktop themes.