feat(desktop): add XDG portals module for in-app screen sharing (task 0030) #22

Merged
alexion merged 1 commits from task-0030-desktop-portals into main 2026-07-22 23:30:22 -04:00
Owner

Task: .claude/tasks/0030-desktop-portals.md

Summary

Adds modules.desktop.portals, enabled by the desktop aggregator, that pins the XDG desktop portal routing explicitly so in-app screen sharing (video calls, browser share) and native file dialogs resolve to a fixed backend.

  • The three interfaces the Hyprland portal implements — ScreenCast, Screenshot, GlobalShortcuts (confirmed from its hyprland.portal file) — route to Hyprland.
  • GTK is the default backend for everything else (file dialogs, appearance).
  • Built config emits /etc/xdg/xdg-desktop-portal/portals.conf with default=gtk plus the three Hyprland routes; neogaia's toplevel builds green.

Deviations

  • The module owns routing, not the backend packages. The Hyprland compositor integration (programs.hyprland → nixpkgs wayland-session.nix) already forces both portal backends (xdg-desktop-portal-hyprland + xdg-desktop-portal-gtk) into xdg.portal.extraPortals and enables portals. A backend only answers while its compositor runs, so those packages belong with the compositor and re-declaring them here would only duplicate. The genuinely-missing piece was the routing (xdg.portal.config was empty), which this module supplies. In-app screen sharing therefore already worked as an implicit side-effect; the deliverable is making it a first-class, aggregator-enabled module.
  • Per-interface routing over a preference list. Chose default = [ "gtk" ] with the three Hyprland interfaces routed explicitly, rather than default = [ "hyprland" "gtk" ]. This keeps appearance on GTK even if a future Hyprland portal starts implementing org.freedesktop.impl.portal.Settings.

Review

Risk

Overall: LOW

  • Blast radius: Low — new self-contained module plus one enable line; only affects portal routing.
  • Reversibility: Low — pure config; delete the module and the enable line to revert; no state or migration.
  • Test coverage: Low — no tests, but it's declarative Nix config where tests add little.
  • Sensitive domain: Low — portals mediate screen-share/file-dialog permissions, but this only sets backend routing, not a security policy or grant.
  • Size & complexity: Low — 21-line module, a static routing set, no control flow.
  • Runtime criticality: Low — desktop-only convenience; misconfiguration degrades screen-share/file-dialog UX, not a production path.

Standards & Spec findings

No unaddressed findings. Standards flagged an over-verbose file header and cross-file narration in the comments; both were fixed (header trimmed to one purpose line, one-sentence-per-line, bare absent-here pointer). Spec confirmed all four acceptance criteria met, and its durability note (appearance could shift off GTK) was addressed by the per-interface routing above.

— Claude

Task: `.claude/tasks/0030-desktop-portals.md` ## Summary Adds `modules.desktop.portals`, enabled by the desktop aggregator, that pins the XDG desktop portal routing explicitly so in-app screen sharing (video calls, browser share) and native file dialogs resolve to a fixed backend. - The three interfaces the Hyprland portal implements — `ScreenCast`, `Screenshot`, `GlobalShortcuts` (confirmed from its `hyprland.portal` file) — route to Hyprland. - GTK is the default backend for everything else (file dialogs, appearance). - Built config emits `/etc/xdg/xdg-desktop-portal/portals.conf` with `default=gtk` plus the three Hyprland routes; neogaia's toplevel builds green. ## Deviations - **The module owns routing, not the backend packages.** The Hyprland compositor integration (`programs.hyprland` → nixpkgs `wayland-session.nix`) already forces both portal backends (`xdg-desktop-portal-hyprland` + `xdg-desktop-portal-gtk`) into `xdg.portal.extraPortals` and enables portals. A backend only answers while its compositor runs, so those packages belong with the compositor and re-declaring them here would only duplicate. The genuinely-missing piece was the routing (`xdg.portal.config` was empty), which this module supplies. In-app screen sharing therefore already worked as an implicit side-effect; the deliverable is making it a first-class, aggregator-enabled module. - **Per-interface routing over a preference list.** Chose `default = [ "gtk" ]` with the three Hyprland interfaces routed explicitly, rather than `default = [ "hyprland" "gtk" ]`. This keeps appearance on GTK even if a future Hyprland portal starts implementing `org.freedesktop.impl.portal.Settings`. ## Review ### Risk **Overall: LOW** - Blast radius: Low — new self-contained module plus one enable line; only affects portal routing. - Reversibility: Low — pure config; delete the module and the enable line to revert; no state or migration. - Test coverage: Low — no tests, but it's declarative Nix config where tests add little. - Sensitive domain: Low — portals mediate screen-share/file-dialog permissions, but this only sets backend routing, not a security policy or grant. - Size & complexity: Low — 21-line module, a static routing set, no control flow. - Runtime criticality: Low — desktop-only convenience; misconfiguration degrades screen-share/file-dialog UX, not a production path. ### Standards & Spec findings No unaddressed findings. Standards flagged an over-verbose file header and cross-file narration in the comments; both were fixed (header trimmed to one purpose line, one-sentence-per-line, bare absent-here pointer). Spec confirmed all four acceptance criteria met, and its durability note (appearance could shift off GTK) was addressed by the per-interface routing above. — Claude
alexion added 1 commit 2026-07-22 23:28:17 -04:00
Add modules.desktop.portals, enabled by the desktop aggregator, pinning the
XDG desktop portal routing explicitly: the three interfaces the Hyprland
portal implements (screencast, screenshot, global shortcuts) go to Hyprland,
and GTK is the default for file dialogs and appearance.

The backend packages already arrive with the Hyprland compositor integration,
so this module owns only the routing, which was previously empty and rode on
the config file the Hyprland package happens to ship. Making it a first-class
module so in-app screen sharing does not depend on that incidental default.
alexion merged commit ac96639c20 into main 2026-07-22 23:30:22 -04:00
alexion deleted branch task-0030-desktop-portals 2026-07-22 23:30:22 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: alexion/dotfiles#22