feat(desktop): add the desktop group and Hyprland session (task 0021) #13

Merged
alexion merged 5 commits from task-0021-desktop-group-and-hyprland-session into main 2026-07-22 10:09:37 -04:00
Owner

Task: .claude/tasks/0021-desktop-group-and-hyprland-session.md

Summary

The tracer bullet for the whole desktop: a keyboard-driven Hyprland session neogaia can log into and open a terminal in.

Adds the modules/desktop/ group behind a single modules.desktop.enable aggregator that hand-lists and turns on each piece at default priority, so a host enables the whole desktop with one flag yet can still override any single piece.

  • hyprland/hyprland.nix — the compositor, in a subdirectory of the group. Sourced from nixpkgs, with programs.hyprland (withUWSM) owning the session, portals, and polkit while home-manager writes the config against that one package (package = null). Ported hjkl keybinds (focus, window move, resize, workspaces 1–9, terminal, floating, fullscreen, split, close, force-kill), input tuning (US layout, Caps→Escape, snappy repeat, touchpad taps + natural scroll + disable-while-typing, flat mouse accel), subtle animations with modest rounding and small gaps, and blur as a host-overridable knob (off here).
  • login.nix — greetd + tuigreet text login, launching the session through uwsm.
  • terminal.nix — Ghostty, opened on Super+Return.
  • neogaia enables modules.desktop; builds green under the flake check.

This branch is cherry-picked off main; it also carries a docs: commit adding the spec, ADR 0003 (compositor choice), and the task breakdown (0021–0030).

Deviations

  • Keybind scope. Only the compositor-native bindings the task enumerates are ported. The spec table's launcher, lock, screenshot, clipboard, and record bindings depend on tools from later tasks (0024–0029), so each of those tasks adds its own binding rather than this one binding to a missing binary.
  • hyprlang, not Lua. The home-manager Hyprland module now defaults configType to "lua" at stateVersion ≥ 26.05, which serialises $mod variables and bind= strings into invalid Lua without failing the build. Pinned to configType = "hyprlang"; recorded as a CLAUDE.md gotcha.
  • Dropped. Mouse drag move/resize (bindm) removed as outside the enumerated keyboard bindings. Hardware media/brightness keys deferred, as they depend on audio and backlight tooling not yet in scope.

Review

Overall: MEDIUM

  • Blast radius: Low — self-contained new module group, gated behind one flag, enabled on a single host; no existing code paths altered.
  • Reversibility: Low — additive only; drop the flag or the files to revert, no migrations.
  • Test coverage: Medium — nix flake check gates build/eval, but a graphical session cannot be exercised headless.
  • Sensitive domain: Medium — touches the login path (greetd/tuigreet, uwsm) and compositor privilege wiring (polkit/portals); a misconfig can lock out interactive login on the host.
  • Size & complexity: Low — small, declarative config across new files, straightforward control flow.
  • Runtime criticality: Medium — the laptop's primary interactive desktop and login, though SSH/console access remains.

Standards / Spec findings left unaddressed: none. Every Standards finding (three semicolons-in-comments, multi-line/alternative-justifying file headers, cross-file narration, and the out-of-scope bindm binds) was fixed in the diff. The Spec axis reported the implementation fully faithful.

— Claude

Task: `.claude/tasks/0021-desktop-group-and-hyprland-session.md` ## Summary The tracer bullet for the whole desktop: a keyboard-driven Hyprland session neogaia can log into and open a terminal in. Adds the `modules/desktop/` group behind a single `modules.desktop.enable` aggregator that hand-lists and turns on each piece at default priority, so a host enables the whole desktop with one flag yet can still override any single piece. - **`hyprland/hyprland.nix`** — the compositor, in a subdirectory of the group. Sourced from nixpkgs, with `programs.hyprland` (`withUWSM`) owning the session, portals, and polkit while home-manager writes the config against that one package (`package = null`). Ported hjkl keybinds (focus, window move, resize, workspaces 1–9, terminal, floating, fullscreen, split, close, force-kill), input tuning (US layout, Caps→Escape, snappy repeat, touchpad taps + natural scroll + disable-while-typing, flat mouse accel), subtle animations with modest rounding and small gaps, and `blur` as a host-overridable knob (off here). - **`login.nix`** — greetd + tuigreet text login, launching the session through uwsm. - **`terminal.nix`** — Ghostty, opened on `Super+Return`. - neogaia enables `modules.desktop`; builds green under the flake check. This branch is cherry-picked off `main`; it also carries a `docs:` commit adding the spec, ADR 0003 (compositor choice), and the task breakdown (0021–0030). ### Deviations - **Keybind scope.** Only the compositor-native bindings the task enumerates are ported. The spec table's launcher, lock, screenshot, clipboard, and record bindings depend on tools from later tasks (0024–0029), so each of those tasks adds its own binding rather than this one binding to a missing binary. - **hyprlang, not Lua.** The home-manager Hyprland module now defaults `configType` to `"lua"` at stateVersion ≥ 26.05, which serialises `$mod` variables and `bind=` strings into invalid Lua without failing the build. Pinned to `configType = "hyprlang"`; recorded as a `CLAUDE.md` gotcha. - **Dropped.** Mouse drag move/resize (`bindm`) removed as outside the enumerated keyboard bindings. Hardware media/brightness keys deferred, as they depend on audio and backlight tooling not yet in scope. ## Review **Overall: MEDIUM** - Blast radius: Low — self-contained new module group, gated behind one flag, enabled on a single host; no existing code paths altered. - Reversibility: Low — additive only; drop the flag or the files to revert, no migrations. - Test coverage: Medium — `nix flake check` gates build/eval, but a graphical session cannot be exercised headless. - Sensitive domain: Medium — touches the login path (greetd/tuigreet, uwsm) and compositor privilege wiring (polkit/portals); a misconfig can lock out interactive login on the host. - Size & complexity: Low — small, declarative config across new files, straightforward control flow. - Runtime criticality: Medium — the laptop's primary interactive desktop and login, though SSH/console access remains. **Standards / Spec findings left unaddressed:** none. Every Standards finding (three semicolons-in-comments, multi-line/alternative-justifying file headers, cross-file narration, and the out-of-scope `bindm` binds) was fixed in the diff. The Spec axis reported the implementation fully faithful. — Claude
alexion added 2 commits 2026-07-22 08:03:39 -04:00
Add the hyprland-desktop spec, ADR 0003 recording the compositor choice
over Sway/KDE/niri, and the task breakdown (0021-0030) that decomposes
the desktop into granular grouped modules. Record the corrected host GPU
facts (zeus is AMD, raichu the sole Nvidia machine) in CLAUDE.md.
Introduce the modules/desktop/ group behind a single modules.desktop.enable
aggregator that hand-lists and turns on each piece at default priority, so a
host enables the whole desktop with one flag yet can override any single piece.

The Hyprland compositor lives in a subdirectory of the group, sourced from
nixpkgs with the program integration owning the session, portals, and polkit
and home-manager writing the config against that one package. greetd with the
tuigreet greeter provides a mouse-free text login that launches the session
through uwsm, and Ghostty is the terminal on Super+Return.

Port the operator's KDE/i3 bindings expressed entirely in hjkl and letters,
tune input (US layout, Caps->Escape, snappy repeat, touchpad taps and natural
scroll, flat mouse accel), and set a subtle feel with blur left as a host knob.
Enable the desktop on neogaia.
alexion added 1 commit 2026-07-22 09:02:58 -04:00
Two issues surfaced on the live neogaia session. Hyprland rejected two names
at load: the split toggle is a dwindle layout message reached through
layoutmsg, not a top-level dispatcher, and dwindle:pseudotile no longer exists
in this version. Bind Super+T via layoutmsg and drop the pseudotile option.

Ghostty also cold-started GTK on every launch. Configure it through
home-manager with gtk-single-instance so windows after the first open in the
existing process. Record the dispatcher/option version-drift trap in CLAUDE.md.
alexion added 1 commit 2026-07-22 09:46:37 -04:00
Ghostty's GTK4 window construction made every launch sluggish on neogaia's
integrated graphics (~440 ms to map a window), which a head-to-head
comparison against Alacritty confirmed. Alacritty's lightweight OpenGL
renderer opens fast on the iGPU, so it becomes the terminal on Super+Return.

The choice stays reversible per host, so a capable host such as zeus could
adopt Ghostty later.
alexion added 1 commit 2026-07-22 10:08:29 -04:00
Flip between the dwindle and master layouts with one bind. Neither a
dispatcher nor a keyword toggles the layout on its own, so a small pinned
script reads general:layout and sets the other through hyprctl keyword.
alexion merged commit 5ca717ca4a into main 2026-07-22 10:09:37 -04:00
alexion deleted branch task-0021-desktop-group-and-hyprland-session 2026-07-22 10:09:37 -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#13