Files
kyle 3e564d57db Add Omarchy-style Hyprland desktop for straylight
Ports the classic dotfiles Omarchy setup to a no-home-manager NixOS
system module (configs/omarchy.nix), scoped to straylight only so the
other desktop hosts keep i3.

- Hyprland (programs.hyprland) becomes the default lightdm session; i3
  stays enabled and selectable as a fallback.
- Omarchy config layout under ~/.config/{hypr,waybar,rofi,wlogout,
  swayosd,alacritty} symlinked from the store; mutable theme/wallpaper
  state in ~/.config/omarchy/current.
- omarchy-dots generates 8 themes (hyprland/hyprlock/waybar/rofi/
  swayosd/wlogout/alacritty/mako colour files) from each theme's
  palette, with wallpapers fetched from the Omarchy v3.8.4 tag.
- Helper scripts: omarchy-theme-set/bg-next/bg-set/apply, omarchy-menu
  (rofi power/theme/background menus), waybar toggle, screenshot, lock.
- Shared desktop modules (desktop.nix / desktop-light.nix) untouched.
2026-10-14 01:18:23 -07:00

345 lines
14 KiB
Nix

# Omarchy-style Hyprland desktop (classic dotfiles Omarchy, ported to a
# no-home-manager NixOS system module). Imported ONLY by hw/straylight, so
# the other desktop hosts (imladris, ono-sendai) keep i3.
#
# Layout mirrors Omarchy v3:
# ~/.config/{hypr,waybar,rofi,wlogout,swayosd,alacritty} -> read-only store links
# ~/.local/share/omarchy -> store payload (themes/, bin/)
# ~/.config/omarchy/current/ -> mutable state
# (theme + background)
#
# Theme colours are generated at build time from each theme's palette
# (Omarchy themes/<name>/colors.toml) into themes/<name>/{hyprland.conf,
# hyprlock.conf,waybar.css,rofi.rasi,swayosd.css,wlogout.css,alacritty.toml,
# mako.ini}. Wallpapers are fetched from the Omarchy v3.8.4 tag.
{ config, lib, pkgs, ... }:
let
inherit (lib) mkForce mapAttrsToList concatMapStrings;
user = "kyle";
home = "/home/${user}";
omarchyPath = "${home}/.local/share/omarchy";
current = "${home}/.config/omarchy/current";
defaultTheme = "tokyo-night";
# ---- Omarchy themes/*/colors.toml ----
themes = {
tokyo-night = {
accent = "#7aa2f7"; cursor = "#c0caf5"; foreground = "#a9b1d6"; background = "#1a1b26";
selection_foreground = "#c0caf5"; selection_background = "#7aa2f7";
color0 = "#32344a"; color1 = "#f7768e"; color2 = "#9ece6a"; color3 = "#e0af68";
color4 = "#7aa2f7"; color5 = "#ad8ee6"; color6 = "#449dab"; color7 = "#787c99";
color8 = "#444b6a"; color9 = "#ff7a93"; color10 = "#b9f27c"; color11 = "#ff9e64";
color12 = "#7da6ff"; color13 = "#bb9af7"; color14 = "#0db9d7"; color15 = "#acb0d0";
};
catppuccin = {
accent = "#89b4fa"; cursor = "#f5e0dc"; foreground = "#cdd6f4"; background = "#1e1e2e";
selection_foreground = "#1e1e2e"; selection_background = "#f5e0dc";
color0 = "#45475a"; color1 = "#f38ba8"; color2 = "#a6e3a1"; color3 = "#f9e2af";
color4 = "#89b4fa"; color5 = "#f5c2e7"; color6 = "#94e2d5"; color7 = "#bac2de";
color8 = "#585b70"; color9 = "#f38ba8"; color10 = "#a6e3a1"; color11 = "#f9e2af";
color12 = "#89b4fa"; color13 = "#f5c2e7"; color14 = "#94e2d5"; color15 = "#a6adc8";
};
gruvbox = {
accent = "#7daea3"; cursor = "#bdae93"; foreground = "#d4be98"; background = "#282828";
selection_foreground = "#ebdbb2"; selection_background = "#d65d0e";
color0 = "#3c3836"; color1 = "#ea6962"; color2 = "#a9b665"; color3 = "#d8a657";
color4 = "#7daea3"; color5 = "#d3869b"; color6 = "#89b482"; color7 = "#d4be98";
color8 = "#3c3836"; color9 = "#ea6962"; color10 = "#a9b665"; color11 = "#d8a657";
color12 = "#7daea3"; color13 = "#d3869b"; color14 = "#89b482"; color15 = "#d4be98";
};
kanagawa = {
accent = "#7e9cd8"; cursor = "#c8c093"; foreground = "#dcd7ba"; background = "#1f1f28";
selection_foreground = "#c8c093"; selection_background = "#2d4f67";
color0 = "#090618"; color1 = "#c34043"; color2 = "#76946a"; color3 = "#c0a36e";
color4 = "#7e9cd8"; color5 = "#957fb8"; color6 = "#6a9589"; color7 = "#c8c093";
color8 = "#727169"; color9 = "#e82424"; color10 = "#98bb6c"; color11 = "#e6c384";
color12 = "#7fb4ca"; color13 = "#938aa9"; color14 = "#7aa89f"; color15 = "#dcd7ba";
};
everforest = {
accent = "#7fbbb3"; cursor = "#d3c6aa"; foreground = "#d3c6aa"; background = "#2d353b";
selection_foreground = "#2d353b"; selection_background = "#d3c6aa";
color0 = "#475258"; color1 = "#e67e80"; color2 = "#a7c080"; color3 = "#dbbc7f";
color4 = "#7fbbb3"; color5 = "#d699b6"; color6 = "#83c092"; color7 = "#d3c6aa";
color8 = "#475258"; color9 = "#e67e80"; color10 = "#a7c080"; color11 = "#dbbc7f";
color12 = "#7fbbb3"; color13 = "#d699b6"; color14 = "#83c092"; color15 = "#d3c6aa";
};
nord = {
accent = "#81a1c1"; cursor = "#d8dee9"; foreground = "#d8dee9"; background = "#2e3440";
selection_foreground = "#d8dee9"; selection_background = "#4c566a";
color0 = "#3b4252"; color1 = "#bf616a"; color2 = "#a3be8c"; color3 = "#ebcb8b";
color4 = "#81a1c1"; color5 = "#b48ead"; color6 = "#88c0d0"; color7 = "#e5e9f0";
color8 = "#4c566a"; color9 = "#bf616a"; color10 = "#a3be8c"; color11 = "#ebcb8b";
color12 = "#81a1c1"; color13 = "#b48ead"; color14 = "#8fbcbb"; color15 = "#eceff4";
};
rose-pine = {
accent = "#56949f"; cursor = "#cecacd"; foreground = "#575279"; background = "#faf4ed";
selection_foreground = "#575279"; selection_background = "#dfdad9";
color0 = "#f2e9e1"; color1 = "#b4637a"; color2 = "#286983"; color3 = "#ea9d34";
color4 = "#56949f"; color5 = "#907aa9"; color6 = "#d7827e"; color7 = "#575279";
color8 = "#9893a5"; color9 = "#b4637a"; color10 = "#286983"; color11 = "#ea9d34";
color12 = "#56949f"; color13 = "#907aa9"; color14 = "#d7827e"; color15 = "#575279";
};
hackerman = {
accent = "#82FB9C"; cursor = "#ddf7ff"; foreground = "#ddf7ff"; background = "#0B0C16";
selection_foreground = "#0B0C16"; selection_background = "#ddf7ff";
color0 = "#3E4058"; color1 = "#50f872"; color2 = "#4fe88f"; color3 = "#50f7d4";
color4 = "#829dd4"; color5 = "#86a7df"; color6 = "#7cf8f7"; color7 = "#85E1FB";
color8 = "#6a6e95"; color9 = "#85ff9d"; color10 = "#9cf7c2"; color11 = "#a4ffec";
color12 = "#c4d2ed"; color13 = "#cddbf4"; color14 = "#d1fffe"; color15 = "#ddf7ff";
};
};
# ---- Omarchy themes/<name>/backgrounds/<file> (v3.8.4) ----
wallpapers = {
tokyo-night = [
{ file = "1-sunset-lake.png"; hash = "sha256-Bt+5/OAp7DWttA6jk5KWd3wrFA0SMrWhaXsVBS1XU2I="; }
{ file = "2-pawel-czerwinski.jpg"; hash = "sha256-acxN1KQHwH9VRgD549BP7AKTuMiF3dhR/lg+/C6ty1I="; }
{ file = "4-oma-cityscape.jpg"; hash = "sha256-IuHWOXIrM/pvDTrCczRQtLrpYbSB9WZcsWOXvF9xTlw="; }
];
catppuccin = [
{ file = "1-totoro.png"; hash = "sha256-O8+rFOjnsEEwfLd9cAxpWRYP5/bV3mMbFb0C3gz42f8="; }
{ file = "2-waves.png"; hash = "sha256-VjGQ35WJvrLVEX1A2wLGSSMqtrw8Py622Xqfs1haDP0="; }
];
gruvbox = [
{ file = "1-the-backwater.jpg"; hash = "sha256-E8/GrjcK+YTAioIP+BtWoP67D7/pcSptsSi5TRTsqsY="; }
{ file = "4-idyllic-procession.jpg"; hash = "sha256-vgrnMmOl62lBb7E1R4JqYIBoWaoxuwMQU6/HPeYKVcU="; }
{ file = "5-leaves.jpg"; hash = "sha256-zb0817NKLI5+dIGK9RAahTF4aHiwo6/Lj2mFsnOU9m0="; }
];
kanagawa = [
{ file = "1-kanagawa.jpg"; hash = "sha256-m4F9cXaIU58NuHYDtmGZKDnDy7XUkYoCpO/AXJ+Isn0="; }
];
everforest = [
{ file = "1-tree-tops.jpg"; hash = "sha256-DwCl4tD3jV51x6L0gQH23uaRecbZTp9OvG+3wPOUYr8="; }
];
nord = [
{ file = "1-city-view.png"; hash = "sha256-sRhvp+5OnZAIe4aTTE3jGoZ9qkojxAGIUYGwe6Ev/3U="; }
{ file = "2-night-hawks.png"; hash = "sha256-YaS8ScE3ERId6H9ffyQH/4I1BRzRf5ze1+IOFbGdVpI="; }
];
rose-pine = [
{ file = "1-funky-shapes.jpg"; hash = "sha256-b1qeo9wiP76RBGfMc5Z7pt2ffoCn2Aa8FemFVj0V/gM="; }
];
hackerman = [
{ file = "1-synth-scape.jpg"; hash = "sha256-x9JZSTXyBODfG0Pf56EjieocHT/Y5XqSqfCEl/J6N34="; }
];
};
# ---- helpers ----
hexDigits = {
"0" = 0; "1" = 1; "2" = 2; "3" = 3; "4" = 4; "5" = 5; "6" = 6; "7" = 7;
"8" = 8; "9" = 9; "a" = 10; "b" = 11; "c" = 12; "d" = 13; "e" = 14; "f" = 15;
};
hexToRgb = hex:
let
s = lib.toLower (lib.removePrefix "#" hex);
dig = i: hexDigits.${builtins.substring i 1 s};
byte = i: (dig (i * 2)) * 16 + (dig (i * 2 + 1));
in "${toString (byte 0)},${toString (byte 1)},${toString (byte 2)}";
strip = hex: lib.removePrefix "#" hex;
mkFile = name: text: pkgs.writeText name text;
themeDir = name: t:
let
bgs = wallpapers.${name} or [ ];
hyprlandConf = mkFile "hyprland.conf" ''
$activeBorderColor = rgb(${strip t.accent})
general {
col.active_border = $activeBorderColor
}
group {
col.border_active = $activeBorderColor
}
'';
hyprlockConf = mkFile "hyprlock.conf" ''
$color = rgba(${hexToRgb t.background}, 1.0)
$inner_color = rgba(${hexToRgb t.background}, 0.8)
$outer_color = rgba(${hexToRgb t.foreground}, 1.0)
$font_color = rgba(${hexToRgb t.foreground}, 1.0)
$check_color = rgba(${hexToRgb t.accent}, 1.0)
'';
waybarCss = mkFile "waybar.css" ''
@define-color foreground ${t.foreground};
@define-color background ${t.background};
'';
swayosdCss = mkFile "swayosd.css" ''
@define-color background ${t.background};
@define-color border-color ${t.foreground};
@define-color label ${t.foreground};
@define-color image ${t.foreground};
@define-color progress ${t.accent};
'';
wlogoutCss = mkFile "wlogout.css" ''
@define-color background ${t.background};
@define-color foreground ${t.foreground};
@define-color accent ${t.accent};
'';
rofiRasi = mkFile "rofi.rasi" ''
* {
bg: ${t.background};
fg: ${t.foreground};
accent: ${t.accent};
background-color: @bg;
text-color: @fg;
border: 2px;
border-color: @accent;
border-radius: 0;
padding: 8px;
spacing: 6px;
font: "JetBrainsMono Nerd Font 12";
}
window { width: 40%; background-color: @bg; border: 2px; border-color: @accent; }
inputbar { background-color: @bg; text-color: @fg; }
listview { lines: 10; background-color: @bg; }
element { padding: 4px; }
element selected { background-color: @accent; text-color: @bg; }
'';
alacrittyToml = mkFile "alacritty.toml" ''
[colors.primary]
background = "${t.background}"
foreground = "${t.foreground}"
[colors.cursor]
text = "${t.background}"
cursor = "${t.cursor}"
[colors.vi_mode_cursor]
text = "${t.background}"
cursor = "${t.cursor}"
[colors.selection]
text = "${t.selection_foreground}"
background = "${t.selection_background}"
[colors.normal]
black = "${t.color0}"
red = "${t.color1}"
green = "${t.color2}"
yellow = "${t.color3}"
blue = "${t.color4}"
magenta = "${t.color5}"
cyan = "${t.color6}"
white = "${t.color7}"
[colors.bright]
black = "${t.color8}"
red = "${t.color9}"
green = "${t.color10}"
yellow = "${t.color11}"
blue = "${t.color12}"
magenta = "${t.color13}"
cyan = "${t.color14}"
white = "${t.color15}"
'';
makoIni = mkFile "mako.ini" ''
text-color=${t.foreground}
border-color=${t.accent}
background-color=${t.background}
width=420
height=110
padding=10
margin=10
border-size=2
border-radius=0
anchor=top-right
layer=overlay
default-timeout=5000
max-visible=5
font=JetBrainsMono Nerd Font 10
'';
in
pkgs.runCommand "omarchy-theme-${name}" { } ''
mkdir -p $out/backgrounds
cp ${hyprlandConf} $out/hyprland.conf
cp ${hyprlockConf} $out/hyprlock.conf
cp ${waybarCss} $out/waybar.css
cp ${swayosdCss} $out/swayosd.css
cp ${wlogoutCss} $out/wlogout.css
cp ${rofiRasi} $out/rofi.rasi
cp ${alacrittyToml} $out/alacritty.toml
cp ${makoIni} $out/mako.ini
${concatMapStrings (b: ''
cp ${pkgs.fetchurl {
url = "https://raw.githubusercontent.com/omacom/omarchy/v3.8.4/themes/${name}/backgrounds/${b.file}";
hash = b.hash;
}} $out/backgrounds/${b.file}
'') bgs}
'';
themesTree = pkgs.linkFarm "omarchy-themes"
(mapAttrsToList (n: t: { name = n; path = themeDir n t; }) themes);
omarchyDots = pkgs.runCommand "omarchy-dots" { } ''
mkdir -p $out
cp -r ${./omarchy/hypr} $out/hypr
cp -r ${./omarchy/waybar} $out/waybar
cp -r ${./omarchy/rofi} $out/rofi
cp -r ${./omarchy/wlogout} $out/wlogout
cp -r ${./omarchy/swayosd} $out/swayosd
cp -r ${./omarchy/alacritty} $out/alacritty
cp -r ${themesTree} $out/themes
'';
omarchyBin = pkgs.runCommand "omarchy-bin" { } ''
mkdir -p $out/bin
cp ${./omarchy/bin}/* $out/bin/
chmod +x $out/bin/*
'';
polkitAgent = pkgs.writeShellScriptBin "omarchy-polkit-agent" ''
exec ${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1
'';
in
{
programs.hyprland = {
enable = true;
systemd.setPath.enable = true;
};
# Hyprland is the default login session. i3 stays enabled (configs/desktop-light.nix)
# and remains selectable in lightdm as a fallback.
services.displayManager.defaultSession = "hyprland";
# Hyprland's own portal should be the default (desktop-light.nix sets gtk).
xdg.portal = {
config.common.default = mkForce "hyprland";
extraPortals = [ pkgs.xdg-desktop-portal-hyprland ];
};
environment.systemPackages = with pkgs; [
omarchyBin
polkitAgent
waybar rofi wlogout hyprlock hypridle swayosd mako clipse
wl-clipboard wl-clip-persist grim slurp hyprpicker
brightnessctl pamixer playerctl alacritty swaybg jq
gnome-calculator pavucontrol nautilus
nerd-fonts.jetbrains-mono
];
# Link the read-only config into ~/.config, seed the mutable theme state, and
# resolve the active theme/background symlinks. Runs on every switch/boot.
system.activationScripts.omarchy-dots = {
deps = [ "users" ];
text = ''
set -eu
export PATH=${lib.makeBinPath [ pkgs.coreutils pkgs.findutils pkgs.bash ]}:$PATH
install -d -m0755 -o ${user} -g users ${home}/.config ${home}/.local/share ${current}
ln -sfnT ${omarchyDots}/hypr ${home}/.config/hypr
ln -sfnT ${omarchyDots}/waybar ${home}/.config/waybar
ln -sfnT ${omarchyDots}/rofi ${home}/.config/rofi
ln -sfnT ${omarchyDots}/wlogout ${home}/.config/wlogout
ln -sfnT ${omarchyDots}/swayosd ${home}/.config/swayosd
ln -sfnT ${omarchyDots}/alacritty ${home}/.config/alacritty
ln -sfnT ${omarchyDots} ${omarchyPath}
[ -f ${current}/theme.name ] || printf '%s\n' ${defaultTheme} > ${current}/theme.name
chown -R ${user}:users ${home}/.config/omarchy
HOME=${home} OMARCHY_PATH=${omarchyPath} ${omarchyBin}/bin/omarchy-theme-apply
chown -h ${user}:users ${current}/theme ${current}/background 2>/dev/null || true
'';
};
}