fix(desktop): file dialog on the GTK backend, one portal config

useNautilus = false assigns FileChooser to gtk and drops the nautilus D-Bus
registration; the GTK3 dialog then follows the same settings.ini as thunar.

The deployed niri-portals.conf is gone: /etc/xdg-desktop-portal/ ranks below
/etc/xdg/, where programs.niri writes the same file. Its one extra key,
Inhibit=none, moves to desktop.nix.
This commit is contained in:
nevaforget committed 2026-09-25 13:10:16 +02:00
1 parent 3007656b0f
commit d26faa091d
3 files changed
+98 -5

No files matched your search

+56
View File
@@ -1,5 +1,61 @@
# Decisions
## 2026-09-25 – One portal config, and the file dialog on the GTK backend
- **Who**: Dominik, ClaudeCode
- **Why**: Firefox's save dialog was drawn in a style that belongs to nothing
else on the system — Adwaita buttons and colours — while thunar, the file
manager, was themed. Two causes, one behind the other.
The dialog is not Firefox's own: under Wayland it is requested through
`org.freedesktop.impl.portal.FileChooser`, and that interface was unassigned,
so the `default=gnome;gtk` line handed it to `xdg-desktop-portal-gnome`. That
backend delegates to nautilus, which nixpkgs' niri module registers with D-Bus
although the binary is on no `PATH`. Nautilus is GTK4 and libadwaita, and
libadwaita draws its own style without reading `gtk-theme-name` from
`settings.ini` — so no amount of theming in `modules/theme.nix` could have
reached it.
Behind that: the portal assignment was written in two places. `desktop.nix`
deployed moonarch's `niri-portals.conf` to `/etc/xdg-desktop-portal/`, and
`programs.niri` had NixOS write its own from `xdg.portal.config.niri` to
`/etc/xdg/xdg-desktop-portal/`. `portals.conf(5)` searches every directory in
`$XDG_CONFIG_DIRS` — `/etc/xdg` among them — before the build-time sysconfdir
`/etc`, so the module's file outranked the deployed one and the file this
repo maintained decided nothing. Both carried the same four keys; moonarch's
stated a fifth, `Inhibit=none;`, whose effect from the lower-ranked file was
never established.
- **Tradeoffs**: (1) **Installing nautilus** would have made the dialog a
deliberate choice rather than an accident, but it stays unthemeable for the
reason above, and it is a second file manager next to thunar. (2) **Keeping
moonarch's file as the source** and generating `xdg.portal.config.niri` from
it would hold the two distros together, at the price of parsing an ini file
in Nix — more code than the assignment itself, for five keys of which four
already match what the module sets. (3) **Leaving the deployed copy in place**
and only fixing the dialog was rejected: it would have left a file in the tree
that looks like the place to change the portal assignment and is not.
- **How**: The `environment.etc` entry for `niri-portals.conf` is gone, and with
it the `etc` binding in the `let` block, which had no other reader. The whole
assignment now comes from `xdg.portal.config.niri`: four keys from
`programs.niri`, `FileChooser = "gtk"` from setting
`programs.niri.useNautilus = false`, and
`org.freedesktop.impl.portal.Inhibit = "none"` restated in `desktop.nix` with
moonarch's reasoning carried over in the comment. The option rather than a
config key for the file chooser: only the option also drops the nautilus D-Bus
registration, and setting the key in both places would collide.
Verified on the thinkpad host: the generated
`/etc/xdg/xdg-desktop-portal/niri-portals.conf` carries all six lines, no
second portal config is deployed anywhere, nautilus is out of
`services.dbus.packages`, and the system builds.
`xdg-desktop-portal-gtk` builds the dialog with GTK3 and reads
`/etc/xdg/gtk-3.0/settings.ini`, the same file thunar follows. Screencasting
keeps the gnome backend: niri serves `org.gnome.Mutter.ScreenCast` itself and
that backend is the only caller, which is why it stays in `extraPortals`.
Assigning the file chooser to gtk is also what the niri wiki recommends for a
setup without nautilus.
## 2026-09-24 – One topic per module, split out of desktop.nix
- **Who**: Dominik, ClaudeCode
+1
View File
@@ -280,6 +280,7 @@ NixOS module owns the destination:
| `gtklock/config.ini` | generated, not copied: background and module paths are store paths. Deployed to `/etc/xdg`, which gtklock reads — no `$HOME` seed |
| `stasis/stasis.rune` | copied unchanged and seeded into `$HOME`; its four action lines name `moonarch-lock`, which `moonarch-scripts` installs |
| `waypaper/config.ini` | wallpaper path, rewritten and seeded into `$HOME` |
| `xdg-desktop-portal/niri-portals.conf` | not deployed at all: `programs.niri` writes the same file from `xdg.portal.config.niri` to `/etc/xdg`, which `portals.conf(5)` ranks above the `/etc` copy. The one key moonarch states beyond the module's four is set in `desktop.nix` |
| fontconfig defaults | `/etc/fonts/conf.d` is owned by the NixOS module |
`kanshi/config` is not deployed and kanshi is not installed: niri configures its
+41 -5
View File
@@ -5,8 +5,6 @@
let
xdg = "${moonarchSrc}/defaults/xdg";
etc = "${moonarchSrc}/defaults/etc";
# waypaper calls `swww`/`swww-daemon`, but the maintained successor ships as
# `awww`. The symlinks keep the shared config working unchanged.
swww-compat = pkgs.runCommand "swww-compat" { } ''
@@ -70,9 +68,13 @@ in
# No stasis entry: /etc/stasis/ is only its fallback for a missing user
# config, and the seed below means there never is one.
# Configs that live outside defaults/xdg/ in moonarch as well.
"xdg-desktop-portal/niri-portals.conf".source =
"${etc}/xdg-desktop-portal/niri-portals.conf";
# No xdg-desktop-portal entry: moonarch's niri-portals.conf would be
# deployed to /etc/xdg-desktop-portal/, and portals.conf(5) searches that
# path — the build-time sysconfdir — only after every directory in
# $XDG_CONFIG_DIRS. /etc/xdg is one of those, and it is where NixOS writes
# the same file from xdg.portal.config.niri, which programs.niri defines.
# The deployed copy would rank below it and decide nothing. What it carries
# is set under Portals below instead.
};
# --- Compositor ---
@@ -97,10 +99,44 @@ in
];
# --- Portals ---
#
# The whole assignment lives here, in the one file NixOS writes. programs.niri
# defines xdg.portal.config.niri with default=gnome;gtk and Access,
# FileChooser, Notification and Secret — the same four moonarch's
# niri-portals.conf carries, which is why that file is not deployed; see the
# note in environment.etc above. Only what moonarch states beyond them is
# restated below.
xdg.portal = {
enable = true;
extraPortals = with pkgs; [ xdg-desktop-portal-gnome xdg-desktop-portal-gtk ];
# Carried over from moonarch's file verbatim, its reasoning included:
# xdg-desktop-portal-gtk reports the Inhibit interface as success even
# though nothing implements it under niri, which makes Firefox skip the
# native Wayland idle-inhibit. With no backend the browser falls back to
# zwp_idle_inhibit, which niri honors, so windowed video keeps the screen
# awake. programs.niri leaves this interface unassigned, so stating it here
# does not collide with the module's definition.
config.niri."org.freedesktop.impl.portal.Inhibit" = "none";
};
# The file dialog every portal client opens — Firefox's save dialog among
# them. The option defaults to true, which registers nautilus with D-Bus and
# leaves org.freedesktop.impl.portal.FileChooser unassigned, so the
# default=gnome;gtk line hands it to xdg-desktop-portal-gnome and the dialog
# is nautilus' own: GTK4 and libadwaita, which draw their own style and never
# read gtk-theme-name out of settings.ini. That is why the dialog came up in
# Adwaita while thunar, a GTK3 program reading /etc/xdg/gtk-3.0/settings.ini,
# was themed.
#
# Off, the module assigns the interface to gtk and drops the nautilus D-Bus
# registration; xdg-desktop-portal-gtk then builds the dialog with GTK3 from
# that same settings.ini. It is set through the option rather than through
# config.niri above because only the option covers the D-Bus half — and
# setting the key in both places would collide. Screencasting is unaffected:
# niri serves org.gnome.Mutter.ScreenCast itself and only the gnome backend
# calls it, which is why that one stays in extraPortals.
programs.niri.useNautilus = false;
environment.systemPackages = with pkgs; [
# XDG plumbing, before anything that calls into it. xdg-terminal-exec
# resolves a caller to whichever TerminalEmulator entry the XDG data