feat: unify monospace typography and style the One Ring greeter

This commit is contained in:
OpenAI Coding Assistant
2026-09-06 13:23:51 -05:00
parent 9c32b3f877
commit b0f6742195
12 changed files with 226 additions and 30 deletions
+4
View File
@@ -5,6 +5,8 @@ Reviewed 2026-09-06 UTC on the physical laptop and against the pinned upstream s
## Design
- Dark **One Ring** wallpaper, charcoal surfaces, parchment text and restrained old-gold accents. Terminal ANSI colors remain distinct; Neovim's theme and configuration are unchanged.
- **JetBrains Mono Nerd Font** throughout the system/UI defaults, with Noto emoji/CJK fallbacks. GTK, Qt's GTK integration, native desktop components and the lock screen share it; explicit website fonts and Tor Browser privacy settings are not overridden.
- **SDDM Astronaut** is styled with the same static wallpaper, gold accents and monospace typography. The left-side login form leaves the artwork visible, with no animated background or blur. Only the greeter's presentation changes: no host autologin, PAM/password edits or bootloader changes.
- One continuous top bar: launcher/workspaces/help and active-window context on the left, date/time in the center, media, CPU/RAM/temperature, notification count, privacy and laptop status on the right. Details and actions are available without filling the bar with permanent buttons.
- The bar's quick-settings panel owns audio, microphone, brightness, network/VPN, Bluetooth, power profiles and idle inhibition. Its **Actions** and **Health** buttons open the action palette and real system/update diagnostics.
- SwayNC owns notification history, actions, DND and media—not a duplicate hardware settings dashboard.
@@ -62,6 +64,8 @@ The native audit caught real issues that configuration evaluation did not: neste
`desktop-test.py` exercises the disposable graphical VM using the same SDDM/UWSM workstation module as the laptop: session ownership, fonts, real PipeWire nodes, launcher geometry at 100%/150%, clipboard picker, recording container and no-audio default, notification ownership/inhibition, described help, and real wrong/correct-password PAM locking. Test credentials never reach the host. `desktop-actions-test.py` covers cancellation, untrusted input, byte-preserving clipboard behavior, lock failure handling, display timeout restoration and recording-inhibitor cleanup.
`nix build .#checks.x86_64-linux.appearance --out-link /tmp/workstation-appearance` checks generic font matching, emoji/CJK fallback and shared UI settings, then renders the actual packaged Qt6 SDDM greeter in test mode inside a disposable 1920×1080 Xvfb display. Its `greeter.png` and `greeter.log` are suitable for review without logging out, restarting SDDM or authenticating anyone. This is a theme rendering check, not a new real-password login test.
**Completed validation (2026-09-06):** flake evaluation, Nix formatting, generated Hyprland configuration, physical/AWS/shared-policy assertions, 125 CLI executable smoke checks, 14 desktop-action tests, 23 updater regressions, 22 manual-switch regressions (including lock retention through sudo), both host system builds, and the full SDDM/UWSM graphical/PAM/clipboard/recording VM check all passed on the refreshed inputs. Native clipboard, recording and rootless Podman checks passed too. The 150% six-result launcher and help screenshots were inspected visually; the transparent launcher click-catcher's IPC dimensions are not mistaken for the visible palette bounds.
**Account/hardware boundaries:**
+4 -1
View File
@@ -22,7 +22,7 @@ Flat, explicit NixOS modules with locked inputs. Required setup belongs here—n
| `switch-system.sh`, `switch-test.py` | Same installed manual apply/preview command on both hosts, with host identity supplied by Nix |
| `physical-test.nix`, `tools-test.nix`, `git-credentials-test.nix` | Physical/AWS safety, shared-policy assertions, offline tool/help smoke tests and disposable Git credential-cache checks |
| `desktop-test.nix`, `desktop-test.py`, `audit-desktop.sh` | Disposable graphical/PAM/audio/scaling audit |
| `workstation.nix`, `nvidia.nix` | Shared local hardware/SDDM integration for laptop and VM; separate opt-in NVIDIA support |
| `workstation.nix`, `greeter-theme.nix`, `appearance-test.nix`, `nvidia.nix` | Local hardware/SDDM integration, One Ring login theme and isolated rendering/font check; separate opt-in NVIDIA support |
| [DESKTOP.md](DESKTOP.md) | Live audit, wallpaper provenance, JaKooLit comparison and explicit feature-completion plan |
| `agent-vm/`, `templates/agent/`, [AGENT-VM.md](AGENT-VM.md) | Reusable rootless DSH microVM, shared project toolchain, live RW cwd/config/credentials/skills and access/networking guide |
@@ -33,6 +33,7 @@ Flat, explicit NixOS modules with locked inputs. Required setup belongs here—n
- The physical host uses the locally established `dev` password; mutable users preserve it. No plaintext password, private key or password hash is embedded in this repo/store.
- At the user's request, **Plasma and the previously managed `kbot` account are removed**. This configuration does not delete `/home/kbot`; retained files and old generations are not erased. Home Manager only manages `dev`.
- SDDM exposes **Hyprland (uwsm-managed)** only. Plain Hyprland is deliberately hidden because it does not start the session-bound bar, wallpaper, idle and polkit services. UWSM is the single session owner.
- The login greeter uses **SDDM Astronaut**, customized to the One Ring wallpaper, charcoal/parchment/gold palette, JetBrains Mono, a minimal left-hand form and matching cursor. This is a Qt6 login theme, not Plasma. PAM, passwords, session selection and the bootloader are unchanged; host autologin remains disabled.
- Both checkouts are **dev-owned**, including Git metadata: `/etc/nix` on the laptop and `/etc/nixos` on EC2. Existing paths are retained to avoid moving deployed repositories. NixOS maintains ownership without following symlinks into the Nix store; the laptop's original `/etc/nixos` files stay untouched.
- On EC2, AWS boot/storage/networking and SSM/SSH recovery remain intact; no physical greeter, Wi-Fi/Bluetooth or battery services are added. User settings, locale/timezone, packages and update policy come from the same modules. The graphical VM now uses the same SDDM/UWSM integration as the laptop.
- The SSH key in `dev-authorized-keys` is public. Account/cloud/browser/VPN credentials and vaults remain user-controlled.
@@ -63,6 +64,8 @@ Flat, explicit NixOS modules with locked inputs. Required setup belongs here—n
| `direnv allow` | Explicitly approve a project's `.envrc`; no automatic trust whitelist |
| `tmux`, `zellij`, `lazygit` | Persistent terminal workspaces and Git UI; not auto-started or forced |
**JetBrains Mono Nerd Font** is the shared system default for monospace, sans-serif and serif requests, GTK/Qt application UI, bar, launchers, notifications, dialogs and lock/login screens. The family is declared once in `tools.nix`; UI sizes remain 11pt where appropriate and Kitty stays 12pt. Noto fonts preserve emoji and international-character coverage. App/site-specific embedded fonts and Tor Browser's privacy defaults are not forcibly overridden; the Linux text console still uses its bitmap font.
Kitty uses an opaque charcoal background, 12pt JetBrains Mono and compact padding. Font zoom remains `Ctrl-Shift-+` / `Ctrl-Shift--`; reset with `Ctrl-Shift-Backspace`. Semantic ANSI colors remain distinguishable from the gold UI accent. Neovim's own theme is unchanged.
The **informative top bar** includes workspaces/window context, clock, CPU/RAM/host temperature, media, notification count, privacy and laptop status. Click its right-hand status area for audio, microphone, brightness, network/Bluetooth, idle inhibition and power-profile controls. **Actions** and **Health** open the searchable action palette and real system/update diagnostics. Notification history is a separate compact drawer, not another settings dashboard.
+1 -1
View File
@@ -1,4 +1,4 @@
* { font-family: Inter, sans-serif; font-size: 16px; }
* { font-family: "@font@", monospace; font-size: 16px; }
window { background: transparent; }
box.main {
padding: 12px;
+79
View File
@@ -0,0 +1,79 @@
# Render the actual packaged greeter in an isolated X server, never live SDDM.
{ config, pkgs }:
let
inherit (pkgs) lib;
font = "JetBrainsMono Nerd Font";
hm = config.home-manager.users.dev;
theme = import ./greeter-theme.nix { inherit pkgs font; };
sddm = config.services.displayManager.sddm.package.override {
extraPackages = config.services.displayManager.sddm.extraPackages;
};
fontConfig = pkgs.makeFontsConf {
fontDirectories = config.fonts.packages;
impureFontDirectories = [ ];
includes = [ "${config.environment.etc.fonts.source}/conf.d" ];
};
in
assert lib.all (family: builtins.head config.fonts.fontconfig.defaultFonts.${family} == font) [
"sansSerif"
"serif"
"monospace"
];
assert hm.gtk.font.name == font;
assert hm.qt.platformTheme.name == "gtk3";
assert hm.programs.kitty.font.name == font;
assert hm.programs.ashell.settings.appearance.font_name == font;
assert hm.dconf.settings."org/gnome/desktop/interface".font-name == "${font} 11";
assert lib.all (label: label.font_family == font) hm.programs.hyprlock.settings.label;
assert lib.hasInfix font hm.programs.anyrun.extraCss;
assert lib.hasInfix font hm.services.swaync.style;
assert config.services.displayManager.sddm.theme == "sddm-astronaut-theme";
assert config.services.displayManager.sddm.settings.Theme.Font == font;
assert !config.services.displayManager.autoLogin.enable;
assert !config.services.desktopManager.plasma6.enable;
pkgs.runCommand "desktop-appearance-check"
{
nativeBuildInputs = with pkgs; [
fontconfig
xvfb-run
xdotool
imagemagick
gnugrep
];
}
''
export HOME="$TMPDIR/home" XDG_CACHE_HOME="$TMPDIR/cache" XDG_RUNTIME_DIR="$TMPDIR/runtime"
mkdir -m 700 -p "$HOME" "$XDG_CACHE_HOME" "$XDG_RUNTIME_DIR" "$out"
export FONTCONFIG_FILE=${fontConfig}
for family in sans-serif serif monospace; do
fc-match --format='%{family}' "$family" | grep -Fq '${font}'
done
fc-match --format='%{family}' emoji | grep -Fq 'Noto Color Emoji'
fc-match --format='%{family}' ':charset=4e00' | grep -Fq 'Noto Sans CJK'
test -r ${theme}/share/sddm/themes/sddm-astronaut-theme/Backgrounds/one-ring.jpg
export QT_QUICK_BACKEND=software LIBGL_ALWAYS_SOFTWARE=1 QT_QPA_PLATFORM=xcb
xvfb-run -a -s '-screen 0 1920x1080x24' ${pkgs.runtimeShell} -euc '
${sddm}/bin/sddm-greeter-qt6 --test-mode \
--theme ${theme}/share/sddm/themes/sddm-astronaut-theme > "$out/greeter.log" 2>&1 &
pid=$!
trap "kill $pid 2>/dev/null || true" EXIT
for attempt in $(seq 1 40); do
kill -0 "$pid"
if xdotool search --onlyvisible --pid "$pid" > /dev/null 2>&1; then break; fi
sleep 0.5
done
sleep 3
kill -0 "$pid"
xdotool search --onlyvisible --pid "$pid" > /dev/null
magick import -window root "$out/greeter.png"
# Check the focused password field and enabled-button appearance, without login.
window=$(xdotool search --onlyvisible --pid "$pid" | head -n 1)
xdotool windowfocus --sync "$window"
xdotool type --clearmodifiers "preview-only"
sleep 1
magick import -window root "$out/greeter-password.png"
if grep -Ei "(module .* is not installed|failed to load|is not a type|ReferenceError|TypeError|cannot assign)" "$out/greeter.log"; then
exit 1
fi
'
''
+1 -1
View File
@@ -131,7 +131,7 @@ in
programs.zathura = {
enable = true;
options = {
font = "Inter 12";
font = "${builtins.head config.fonts.fontconfig.defaultFonts.monospace} 12";
adjust-open = "best-fit";
zoom-step = 10;
recolor = false; # Preserve actual document colors; Ctrl-R toggles recolor.
+16 -6
View File
@@ -31,7 +31,7 @@ def screenshot(name):
# Capture through Wayland; QEMU's framebuffer dump cannot read VirGL surfaces.
path = "/tmp/" + name + ".png"
session("grim " + shlex.quote(path))
machine.copy_from_vm(path)
machine.copy_from_machine(path)
def launch(name, command):
@@ -58,10 +58,21 @@ except Exception:
raise
wait_layer("ashell-main-layer")
# A headless GPU's preferred mode is not necessarily the requested test size.
# Fix both mode and scale before calling screenshots a 100% geometry audit.
output = json.loads(session("hyprctl -j monitors"))[0]["name"]
session("hyprctl eval " + shlex.quote(
'hl.monitor({output=' + json.dumps(output) + ',mode="1920x1080@60",position="0x0",scale=1})'
))
machine.wait_until_succeeds(in_session(
"hyprctl -j monitors | jq -e '.[0] | .width == 1920 and .height == 1080 and .scale == 1'"
))
screenshot("startup")
assert session("hyprctl configerrors").strip() in ("", "ok")
assert "JetBrainsMono" in user("fc-match 'JetBrainsMono Nerd Font'")
assert "Inter" in user("fc-match Inter")
for family in ["sans-serif", "serif", "monospace"]:
assert "JetBrainsMono" in user("fc-match " + shlex.quote(family))
assert "Noto Color Emoji" in user("fc-match --format='%{family}' emoji")
assert "JetBrainsMono Nerd Font 11" in user("dconf read /org/gnome/desktop/interface/font-name")
# Pi's real --version is checked natively; avoid costly Node startup under TCG.
user("test -x /run/current-system/sw/bin/pi")
assert "zsh" in user("getent passwd dev")
@@ -133,8 +144,6 @@ session("swaync-client -t -sw")
machine.sleep(1)
screenshot("notification-100")
monitors = json.loads(session("hyprctl -j monitors"))
output = monitors[0]["name"]
session("hyprctl eval " + shlex.quote(
'hl.monitor({output=' + json.dumps(output) + ',mode="1920x1080@60",position="0x0",scale=1.5})'
))
@@ -204,5 +213,6 @@ user("test ! -e /etc/profiles/per-user/dev/share/applications/element-desktop.de
user("grep -Eq 'fade_on_empty *= *false' ~/.config/hypr/hyprlock.conf")
assert "libapplications.so" in user("cat ~/.config/anyrun/config.ron")
session("hyprctl clients")
machine.succeed("journalctl -b -p err --no-pager > /tmp/desktop-errors.log")
machine.copy_from_vm("/tmp/desktop-errors.log")
machine.copy_from_machine("/tmp/desktop-errors.log")
+29 -16
View File
@@ -7,11 +7,15 @@
let
c = import ./colors.nix;
font = builtins.head config.fonts.fontconfig.defaultFonts.monospace;
styleTokens = c // {
inherit font;
};
renderColors =
text:
builtins.replaceStrings (map (name: "@${name}@") (
builtins.attrNames c
)) (builtins.attrValues c) text;
builtins.attrNames styleTokens
)) (builtins.attrValues styleTokens) text;
rgb = color: "rgb(${lib.removePrefix "#" color})";
wallpaper = import ./wallpaper.nix { inherit pkgs; };
help = pkgs.writeShellApplication {
@@ -137,17 +141,21 @@ in
];
services.udev.packages = [ pkgs.brightnessctl ];
fonts.packages = with pkgs; [
inter
noto-fonts
noto-fonts-cjk-sans
noto-fonts-color-emoji
];
fonts.fontconfig.defaultFonts = {
# Use the code font for generic UI/document families too, retaining
# international glyph fallbacks rather than replacing missing characters.
sansSerif = [
"Inter"
font
"Noto Sans"
];
serif = [ "Noto Serif" ];
serif = [
font
"Noto Serif"
];
emoji = [ "Noto Color Emoji" ];
};
@@ -169,7 +177,7 @@ in
gtk = {
enable = true;
font = {
name = "Inter";
name = font;
size = 11;
};
theme = {
@@ -186,14 +194,19 @@ in
qt = {
enable = true;
platformTheme.name = "gtk3";
# GTK integration also supplies the same font to Qt 5/6 applications.
# Let Home Manager provide BOTH Qt 5 and Qt 6 style plugins.
style.name = "adwaita-dark";
};
dconf.settings."org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
font-name = "Inter 11";
monospace-font-name = "JetBrainsMono Nerd Font 12";
accent-color = "yellow";
dconf.settings = {
"org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
font-name = "${font} 11";
document-font-name = "${font} 11";
monospace-font-name = "${font} 12";
accent-color = "yellow";
};
"org/gnome/desktop/wm/preferences".titlebar-font = "${font} Bold 11";
};
wayland.windowManager.hyprland = {
@@ -331,7 +344,7 @@ in
audio_indicator_format = "IconAndPercentage";
};
appearance = {
font_name = "Inter";
font_name = font;
scale_factor = 1.15;
style = "Solid";
opacity = 1.0;
@@ -401,7 +414,7 @@ in
enable = true;
settings = {
main = {
font = "Inter:size=11";
font = "${font}:size=11";
terminal = "kitty";
"launch-prefix" = "uwsm app --";
"line-height" = 22;
@@ -524,7 +537,7 @@ in
monitor = "";
text = "cmd[update:1000] date +'%H:%M'";
font_size = 64;
font_family = "Inter";
font_family = font;
color = rgb c.text;
position = "0,100";
halign = "center";
@@ -534,7 +547,7 @@ in
monitor = "";
text = "$USER";
font_size = 18;
font_family = "Inter";
font_family = font;
color = rgb c.muted;
position = "0,0";
halign = "center";
@@ -551,7 +564,7 @@ in
outline_thickness = 2;
dots_center = true;
fade_on_empty = false;
font_family = "Inter";
font_family = font;
rounding = 6;
inner_color = rgb c.surface;
outer_color = rgb c.accent;
+4
View File
@@ -63,6 +63,10 @@
in
{
desktop = import ./desktop-test.nix { inherit inputs pkgs; };
appearance = import ./appearance-test.nix {
inherit pkgs;
config = inputs.self.nixosConfigurations.nixos.config;
};
git-credentials = import ./git-credentials-test.nix {
inherit pkgs;
config = inputs.self.nixosConfigurations.nixos.config;
+65
View File
@@ -0,0 +1,65 @@
# Packaged Qt6 theme, styled with the same wallpaper/palette as the desktop.
# No runtime downloader, custom QML, Plasma desktop, or authentication changes.
{ pkgs, font }:
let
c = import ./colors.nix;
wallpaper = import ./wallpaper.nix { inherit pkgs; };
in
(pkgs.sddm-astronaut.override {
embeddedTheme = "black_hole";
themeConfig = {
Font = font;
FontSize = "11";
HeaderText = "Welcome back";
HourFormat = "HH:mm";
DateFormat = "dddd d MMMM";
Background = "Backgrounds/one-ring.jpg";
DimBackground = "0.15";
CropBackground = "true";
FormPosition = "left";
HaveFormBackground = "true";
PartialBlur = "false";
FullBlur = "false";
RoundCorners = "10";
UseRealName = "false";
HideCompletePassword = "true";
AllowEmptyPassword = "false";
HeaderTextColor = c.accent;
DateTextColor = c.muted;
TimeTextColor = c.text;
FormBackgroundColor = c.background;
BackgroundColor = c.background;
DimBackgroundColor = c.background;
LoginFieldBackgroundColor = c.surface;
PasswordFieldBackgroundColor = c.surface;
LoginFieldTextColor = c.text;
PasswordFieldTextColor = c.text;
PlaceholderTextColor = c.muted;
WarningColor = c.red;
LoginButtonTextColor = c.background;
LoginButtonBackgroundColor = c.accent;
UserIconColor = c.text;
PasswordIconColor = c.text;
SystemButtonsIconsColor = c.text;
SessionButtonTextColor = c.text;
VirtualKeyboardButtonTextColor = c.text;
DropdownTextColor = c.text;
DropdownSelectedBackgroundColor = c.selection;
DropdownBackgroundColor = c.surface;
HighlightTextColor = c.background;
HighlightBackgroundColor = c.accent;
HighlightBorderColor = c.accent;
HoverUserIconColor = c.accent;
HoverPasswordIconColor = c.accent;
HoverSystemButtonsIconsColor = c.accent;
HoverSessionButtonTextColor = c.accent;
HoverVirtualKeyboardButtonTextColor = c.accent;
};
}).overrideAttrs
(old: {
postInstall = (old.postInstall or "") + ''
# The theme resolves background paths relative to its own directory.
chmod u+w "$out/share/sddm/themes/sddm-astronaut-theme/Backgrounds"
ln -s ${wallpaper} "$out/share/sddm/themes/sddm-astronaut-theme/Backgrounds/one-ring.jpg"
'';
})
+1 -1
View File
@@ -1,4 +1,4 @@
* { font-family: Inter, sans-serif; font-size: 14px; color: @text@; }
* { font-family: "@font@", monospace; font-size: 14px; color: @text@; }
.control-center {
background: @background@;
border: 1px solid @border@;
+4 -2
View File
@@ -2,6 +2,7 @@
let
c = import ./colors.nix;
font = "JetBrainsMono Nerd Font";
# Standalone tools from the fast-moving pin, NOT an overlay of the system's
# Python/GCC/libraries. Desktop, drivers and NixOS services remain coherent.
latest = import inputs.nixpkgs-latest {
@@ -229,7 +230,8 @@ in
]);
fonts.packages = [ pkgs.nerd-fonts.jetbrains-mono ];
fonts.fontconfig.defaultFonts.monospace = [ "JetBrainsMono Nerd Font" ];
# Desktop/greeter consumers derive their font from this shared default.
fonts.fontconfig.defaultFonts.monospace = [ font ];
home-manager.users.dev = { config, ... }: {
programs.git = {
@@ -409,7 +411,7 @@ in
programs.kitty = {
enable = true;
font = {
name = "JetBrainsMono Nerd Font";
name = font;
size = 12;
};
settings = {
+18 -2
View File
@@ -7,6 +7,8 @@
...
}:
let
font = builtins.head config.fonts.fontconfig.defaultFonts.monospace;
greeterTheme = import ./greeter-theme.nix { inherit pkgs font; };
managedHyprlandSession =
pkgs.runCommand "hyprland-managed-session"
{
@@ -28,7 +30,17 @@ in
services.xserver.enable = true;
services.displayManager = {
sddm.enable = true;
sddm = {
enable = true;
package = pkgs.kdePackages.sddm; # Qt6 runtime, not the Plasma desktop.
theme = "sddm-astronaut-theme";
extraPackages = [ greeterTheme ]; # Carries the theme's Qt6 QML dependencies.
settings.Theme = {
Font = font;
CursorTheme = "Bibata-Modern-Ice";
CursorSize = 24;
};
};
defaultSession = "hyprland-uwsm";
# Plain Hyprland bypasses the UWSM-owned bar/idle/polkit services.
sessionPackages = lib.mkForce [ managedHyprlandSession ];
@@ -38,7 +50,11 @@ in
services.gvfs.enable = true;
services.fwupd.enable = true;
home-manager.users.dev.services.udiskie.enable = true;
environment.systemPackages = [ pkgs.networkmanagerapplet ];
environment.systemPackages = [
pkgs.networkmanagerapplet
greeterTheme
pkgs.bibata-cursors
];
hardware.bluetooth = {
enable = true;