feat(greeter): tuigreet + console themed from the state JSON (item 16)
Some checks failed
Check / eval (push) Has been cancelled
Some checks failed
Check / eval (push) Has been cancelled
New modules/nixos/greeter.nix owns the greetd block (moved out of
default.nix) plus the theming, the Plymouth model — baked at SYSTEM
rebuild from nomarchy.system.stateFile. Two parts, because tuigreet
draws on the VT with the 16 ANSI slots: console.colors becomes the
theme's ansi[] (vt.default_* kernel params — which also themes raw
ttys and the LUKS passphrase prompt), and tuigreet gets a --theme of
NAMED slots (its parser is ratatui Color::from_str, checked in source;
the names map to the standard indexes the retinted palette now
serves). A sparse state without ansi skips theming cleanly.
Verified: V0; V1 — the template-system toplevel bakes the full --theme
spec into greetd.toml and all three vt.default_{red,grn,blu} palettes
into kernel-params. A checks.greeter VM test was attempted and dropped
after bisecting: tuigreet dies under runNixOSTest even without the
flag (harness limit; nixpkgs's own greetd test uses agreety) —
recorded in agent/MEMORY.md. Remains: V3 rendering +
theme-follows-rebuild on hardware (HARDWARE-QUEUE).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,10 +83,6 @@ the *same* outputs. **Plan (designed 2026-07-05, iteration #27):**
|
|||||||
- **Non-goals:** no kanshi (fights Hyprland's output management —
|
- **Non-goals:** no kanshi (fights Hyprland's output management —
|
||||||
decided in ROADMAP), nwg-displays stays a find-the-values helper.
|
decided in ROADMAP), nwg-displays stays a find-the-values helper.
|
||||||
|
|
||||||
### 16. Greeter theming from the JSON
|
|
||||||
ROADMAP § Greeter. tuigreet themed from theme-state.json at system
|
|
||||||
rebuild (same model as Plymouth's tint). Scope: tuigreet only (no SDDM).
|
|
||||||
|
|
||||||
### 17. Launch-or-focus UX scripts
|
### 17. Launch-or-focus UX scripts
|
||||||
ROADMAP § launch-or-focus. Hyprland dispatch scripts: a bind launches an
|
ROADMAP § launch-or-focus. Hyprland dispatch scripts: a bind launches an
|
||||||
app or focuses its existing window. Curate which apps get binds;
|
app or focuses its existing window. Curate which apps get binds;
|
||||||
|
|||||||
@@ -153,6 +153,13 @@ QA machine), the **T14s** (webcam case).
|
|||||||
applies the matching profile within ~3s (toast names it);
|
applies the matching profile within ~3s (toast names it);
|
||||||
unplugging switches back; Auto-switch off stops that; with two
|
unplugging switches back; Auto-switch off stops that; with two
|
||||||
profiles naming the same outputs, no flapping (ties do nothing).
|
profiles naming the same outputs, no flapping (ties do nothing).
|
||||||
|
- [ ] **Themed greeter + console (item 16)** — after `sys-rebuild` +
|
||||||
|
logout: tuigreet renders in theme colors (dark container, accent
|
||||||
|
border, themed prompt/time) and a raw tty (CTRL+ALT+F2) shows the
|
||||||
|
theme's ANSI palette; on a LUKS machine the passphrase prompt
|
||||||
|
follows too. Then switch theme + `sys-rebuild` + logout → the
|
||||||
|
greeter follows the new palette. (tuigreet can't run under the
|
||||||
|
VM harness — see MEMORY.md — so rendering is hardware-tier.)
|
||||||
- [ ] **Color picker (item 13, final slice)** — SUPER+CTRL+P (or Tools ›
|
- [ ] **Color picker (item 13, final slice)** — SUPER+CTRL+P (or Tools ›
|
||||||
Color picker): hyprpicker's zoom loupe appears; click a pixel →
|
Color picker): hyprpicker's zoom loupe appears; click a pixel →
|
||||||
toast shows the hex and `wl-paste` yields it; Esc cancels with no
|
toast shows the hex and `wl-paste` yields it; Esc cancels with no
|
||||||
|
|||||||
@@ -17,6 +17,26 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-05 — greeter theming from the JSON (iteration #30, item 16)
|
||||||
|
- **Task:** BACKLOG NEXT#16 — tuigreet themed from theme-state.json at
|
||||||
|
system rebuild (the Plymouth model). Item deleted.
|
||||||
|
- **Did:** new modules/nixos/greeter.nix owns the greetd block (moved
|
||||||
|
out of default.nix, one concern one file) + the theming: (1)
|
||||||
|
console.colors = the theme's ansi[] (mkDefault; lands as
|
||||||
|
vt.default_* kernel params — also themes raw ttys + LUKS prompt);
|
||||||
|
(2) tuigreet --theme on NAMED slots (its parser is ratatui
|
||||||
|
Color::from_str, verified in source; names hit the standard indexes
|
||||||
|
the retinted VT palette now serves). Sparse state w/o ansi → skips
|
||||||
|
cleanly.
|
||||||
|
- **Verified:** V0; V1 — template-system toplevel: baked greetd.toml
|
||||||
|
carries the full --theme spec, kernel-params carry all three
|
||||||
|
vt.default_{red,grn,blu}. A checks.greeter VM test was attempted and
|
||||||
|
DROPPED: tuigreet dies under runNixOSTest even bare (harness limit,
|
||||||
|
not our flag — bisected; nixpkgs tests use agreety) → MEMORY.md.
|
||||||
|
- **Pending:** V3 queued (rendering + theme-follows-rebuild).
|
||||||
|
- **Next suggestion:** #17 launch-or-focus, or the new #28 UI-review
|
||||||
|
audit slice once Bernardo confirms its placement.
|
||||||
|
|
||||||
## 2026-07-05 — display profiles auto-switch (iteration #29, item 15b)
|
## 2026-07-05 — display profiles auto-switch (iteration #29, item 15b)
|
||||||
- **Task:** BACKLOG NEXT#15 slice b — opt-in hotplug auto-switch.
|
- **Task:** BACKLOG NEXT#15 slice b — opt-in hotplug auto-switch.
|
||||||
- **Did:** nomarchy-display-profile gains `match <outputs>` (exact set
|
- **Did:** nomarchy-display-profile gains `match <outputs>` (exact set
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ here the moment a debugging session teaches you something a future
|
|||||||
iteration would otherwise rediscover.
|
iteration would otherwise rediscover.
|
||||||
|
|
||||||
## Testing & VM recipes
|
## Testing & VM recipes
|
||||||
|
- **tuigreet dies silently under runNixOSTest** (even bare, no theme
|
||||||
|
flag: greetd sits as "(greetd)" with no child, nothing in the
|
||||||
|
journal — its stderr goes to the VT) — nixpkgs' own greetd test uses
|
||||||
|
agreety instead. Greeter *rendering* is interactive-ISO/hardware
|
||||||
|
tier; don't burn another session on a checks.greeter VM test.
|
||||||
|
- In VM tests `pgrep -f PATTERN` can match the test backdoor's own
|
||||||
|
`bash -c` wrapper (the pattern is in its cmdline) — use `pgrep -x`
|
||||||
|
or a `[t]uigreet`-style bracket pattern.
|
||||||
- A checks.* fixture CANNOT be a writeText/toFile state file read at
|
- A checks.* fixture CANNOT be a writeText/toFile state file read at
|
||||||
eval time ("path … is not valid" — flake check's eval store won't
|
eval time ("path … is not valid" — flake check's eval store won't
|
||||||
realise it): extract the logic into a pure importable file and
|
realise it): extract the logic into a pure importable file and
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ let
|
|||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./options.nix ./plymouth.nix ./file-manager.nix ./power.nix ./services.nix ./hardware.nix ./timezone.nix ./oom.nix ];
|
imports = [ ./options.nix ./plymouth.nix ./greeter.nix ./file-manager.nix ./power.nix ./services.nix ./hardware.nix ./timezone.nix ./oom.nix ];
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
# Distro branding. distroName flows into /etc/os-release PRETTY_NAME,
|
# Distro branding. distroName flows into /etc/os-release PRETTY_NAME,
|
||||||
@@ -99,22 +99,8 @@ in
|
|||||||
extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; # file pickers, etc.
|
extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; # file pickers, etc.
|
||||||
};
|
};
|
||||||
|
|
||||||
services.greetd = lib.mkIf cfg.greeter.enable {
|
# The greetd/tuigreet login screen lives in ./greeter.nix — themed
|
||||||
enable = lib.mkDefault true;
|
# from the state JSON (console.colors + --theme) at system rebuild.
|
||||||
settings = {
|
|
||||||
default_session = {
|
|
||||||
# start-hyprland is Hyprland 0.55's watchdog launcher; running
|
|
||||||
# the bare binary makes every session print a warning.
|
|
||||||
command = lib.mkDefault "${pkgs.tuigreet}/bin/tuigreet --time --remember --greeting 'Welcome to ${distroName}' --cmd start-hyprland";
|
|
||||||
user = "greeter";
|
|
||||||
};
|
|
||||||
# Boot straight into the session once; logout → normal greeter.
|
|
||||||
initial_session = lib.mkIf (cfg.greeter.autoLogin != null) {
|
|
||||||
command = "start-hyprland";
|
|
||||||
user = cfg.greeter.autoLogin;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ── Audio: Pipewire ──────────────────────────────────────────────
|
# ── Audio: Pipewire ──────────────────────────────────────────────
|
||||||
security.rtkit.enable = lib.mkDefault cfg.audio.enable;
|
security.rtkit.enable = lib.mkDefault cfg.audio.enable;
|
||||||
|
|||||||
70
modules/nixos/greeter.nix
Normal file
70
modules/nixos/greeter.nix
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# Greeter — greetd/tuigreet, themed from the same theme-state.json that
|
||||||
|
# drives the desktop (nomarchy.system.stateFile; the Plymouth model:
|
||||||
|
# baked at SYSTEM rebuild, so it follows the theme as of the last
|
||||||
|
# sys-update, not the last instant apply).
|
||||||
|
#
|
||||||
|
# tuigreet draws on the virtual console with the 16 ANSI slots, so the
|
||||||
|
# theming is two-part:
|
||||||
|
# 1. console.colors — the VT palette becomes the theme's ansi[] hexes
|
||||||
|
# (which also themes raw ttys and the LUKS passphrase prompt: the
|
||||||
|
# same JSON reaches every pre-session surface).
|
||||||
|
# 2. --theme — tuigreet components on NAMED slots (its parser is
|
||||||
|
# ratatui Color::from_str; names map to the standard indexes, e.g.
|
||||||
|
# blue=4, gray=7, white=15, so the palette above hands them the
|
||||||
|
# theme's colors). ANSI "black" stays dark even in light themes —
|
||||||
|
# the greeter reads terminal-dark there, the same convention every
|
||||||
|
# terminal applies to ANSI colors.
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.nomarchy.system;
|
||||||
|
distroName = config.system.nixos.distroName;
|
||||||
|
|
||||||
|
state =
|
||||||
|
if cfg.stateFile != null
|
||||||
|
then builtins.fromJSON (builtins.readFile cfg.stateFile)
|
||||||
|
else { };
|
||||||
|
# A sparse/hand-rolled state without a proper ansi block just skips the
|
||||||
|
# theming (stock tuigreet grey) — never an eval error.
|
||||||
|
ansi = state.ansi or [ ];
|
||||||
|
themed = builtins.isList ansi && builtins.length ansi == 16;
|
||||||
|
|
||||||
|
tuigreetTheme = lib.concatStringsSep ";" [
|
||||||
|
"container=black" # ansi[0] — the theme's terminal background
|
||||||
|
"border=blue" # ansi[4] — the accent family in every shipped palette
|
||||||
|
"title=cyan"
|
||||||
|
"greet=cyan"
|
||||||
|
"prompt=green"
|
||||||
|
"input=white" # ansi[15] — bright foreground
|
||||||
|
"action=blue"
|
||||||
|
"button=yellow"
|
||||||
|
"time=cyan"
|
||||||
|
"text=gray" # ansi[7] — muted foreground
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
# VT palette from the theme (RRGGBB, no #; lands as vt.default_* kernel
|
||||||
|
# params). mkDefault so a downstream console.colors wins.
|
||||||
|
console.colors = lib.mkIf themed (lib.mkDefault (map (lib.removePrefix "#") ansi));
|
||||||
|
|
||||||
|
services.greetd = lib.mkIf cfg.greeter.enable {
|
||||||
|
enable = lib.mkDefault true;
|
||||||
|
settings = {
|
||||||
|
default_session = {
|
||||||
|
# start-hyprland is Hyprland 0.55's watchdog launcher; running
|
||||||
|
# the bare binary makes every session print a warning.
|
||||||
|
command = lib.mkDefault ("${pkgs.tuigreet}/bin/tuigreet --time --remember --greeting 'Welcome to ${distroName}'"
|
||||||
|
+ lib.optionalString themed " --theme '${tuigreetTheme}'"
|
||||||
|
+ " --cmd start-hyprland");
|
||||||
|
user = "greeter";
|
||||||
|
};
|
||||||
|
# Boot straight into the session once; logout → normal greeter.
|
||||||
|
initial_session = lib.mkIf (cfg.greeter.autoLogin != null) {
|
||||||
|
command = "start-hyprland";
|
||||||
|
user = cfg.greeter.autoLogin;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user