feat(system): Plymouth splash, distroName, allowUnfree, offline-pin hardening
- Plymouth boot splash ported from the legacy branch (modules/nixos/ plymouth/): logo + eased progress + LUKS entry, background tinted from theme-state.json via the new nomarchy.system.stateFile (wired by mkFlake/lib.nix; null → Tokyo Night fallback). Default on; OFF on the live ISO (boot-message visibility on the install medium). Pulls boot.initrd.systemd, which also drives the keyboard-at-LUKS feature. - system.nixos.distroName = "Nomarchy" (os-release PRETTY_NAME, systemd-boot entries, ISO menu label). distroId left "nixos" (feeds DEFAULT_HOSTNAME + upstream isNixos checks — roadmapped). - nixpkgs.config.allowUnfree distro-wide (here + both import-nixpkgs sites) — unblocks claude-code for the menu's ask-Claude module. - systemd-boot.configurationLimit = 10 so entries don't fill the ESP. - Live ISO: nomarchy.idle.enable = false — hypridle was suspending the VM mid-install (the install-hung regression); installed systems keep it. - flake.nix offline pins (verified 0-leak via a foreign-identity gap-analysis probe): the repo's own standalone HM gen + inputDerivations, mustache-go + stdenv (stylix re-renders base16 per switch), microcode-amd/ intel (enableRedistributableFirmware activated updateMicrocode → source-build cascade), buildEnv's builder.pl, findXMLCatalogs, and the representativeInstall mirror (xkb/initrd-systemd/microcode). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
32
flake.nix
32
flake.nix
@@ -148,6 +148,7 @@
|
||||
specialArgs = { inherit username; };
|
||||
modules = [
|
||||
self.nixosModules.nomarchy
|
||||
{ nomarchy.system.stateFile = ./theme-state.json; }
|
||||
./hosts/default/configuration.nix
|
||||
];
|
||||
};
|
||||
@@ -251,6 +252,20 @@
|
||||
({ lib, ... }: {
|
||||
nomarchy.system.snapper.enable = true;
|
||||
nomarchy.system.greeter.autoLogin = username;
|
||||
# The installer writes these (keyboard-everywhere
|
||||
# incl. the LUKS prompt) — mirror them or the
|
||||
# initrd/vconsole pins don't match an offline
|
||||
# install's rebuild set.
|
||||
console.useXkbConfig = true;
|
||||
boot.initrd.systemd.enable = true;
|
||||
# nixos-generate-config keys microcode off
|
||||
# enableRedistributableFirmware (now on): the
|
||||
# real machine enables exactly one vendor —
|
||||
# pin both, or the offline install builds
|
||||
# amd-ucode/intel-ucode from source (found the
|
||||
# hard way: the 2700s regression hang).
|
||||
hardware.cpu.amd.updateMicrocode = true;
|
||||
hardware.cpu.intel.updateMicrocode = true;
|
||||
swapDevices = [{ device = "/swap/swapfile"; }];
|
||||
boot.resumeDevice = "/dev/disk/by-uuid/00000000-0000-0000-0000-000000000000";
|
||||
boot.kernelParams = [ "resume_offset=1" ];
|
||||
@@ -298,6 +313,11 @@
|
||||
representativeInstall.config.system.build.etc.inputDerivation
|
||||
representativeInstall.config.system.build.initialRamdisk.inputDerivation
|
||||
template.homeConfigurations.${username}.activationPackage.inputDerivation
|
||||
# home-files COPIES user-dbus-services instead of
|
||||
# referencing it (same story as the self pin above) —
|
||||
# a custom-username install rebuilds home-files and
|
||||
# needs its direct inputs present.
|
||||
template.homeConfigurations.${username}.config.home-files.inputDerivation
|
||||
] ++ [
|
||||
# A theme switch re-renders stylix's base16 templates
|
||||
# (gtk.css, kvantum, …) on the machine: the renderer and
|
||||
@@ -328,6 +348,18 @@
|
||||
# tries to BUILD remarshal, whose test closure pulls
|
||||
# matplotlib/ffmpeg/x265 (the ~1900-drv cascade).
|
||||
pkgs.remarshal
|
||||
# system-path/initrd-bin-env are buildEnvs rebuilt per
|
||||
# machine; their shared builder.pl is build-time-only
|
||||
# (never in any runtime closure) — an empty env's
|
||||
# inputDerivation retains it.
|
||||
(pkgs.buildEnv { name = "nomarchy-pin-buildenv"; paths = [ ]; }).inputDerivation
|
||||
# /etc/dbus-1 reassembly wants this setup hook.
|
||||
pkgs.findXMLCatalogs
|
||||
# nixos-generate-config keys microcode off
|
||||
# enableRedistributableFirmware (now on): exactly one
|
||||
# vendor per real machine, so pin both ucode cpios.
|
||||
pkgs.microcode-amd
|
||||
pkgs.microcode-intel
|
||||
|
||||
# …and one representative disko script: its tool closure
|
||||
# (file, which, wrapper hooks, …) isn't otherwise on the
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
# Ghostty silently falls back to the wrong font (seen on the
|
||||
# Latitude 5410). Normal priority (100) beats the override.
|
||||
fonts.fontconfig.enable = true;
|
||||
|
||||
# No boot splash on the install medium: the installer ISO boots its
|
||||
# own initrd path (squashfs), and visibility of boot messages is
|
||||
# worth more than polish here. Installed systems get the splash.
|
||||
nomarchy.system.plymouth.enable = false;
|
||||
xdg.icons.enable = true;
|
||||
xdg.mime.enable = true;
|
||||
xdg.autostart.enable = true;
|
||||
@@ -85,6 +90,12 @@
|
||||
|
||||
# ── Live-session desktop tweaks ─────────────────────────────────────
|
||||
home-manager.users.${username} = {
|
||||
# No idle lock/suspend on the install medium: an offline install
|
||||
# runs 20-30 min unattended, and hypridle would blank the display
|
||||
# then SUSPEND the machine mid-install (it did — the install-hung
|
||||
# regression). Installed systems keep idle management.
|
||||
nomarchy.idle.enable = false;
|
||||
|
||||
wayland.windowManager.hyprland.settings = {
|
||||
# QEMU (and some panels) report a tiny "preferred" mode; ask for
|
||||
# the highest resolution instead.
|
||||
|
||||
3
lib.nix
3
lib.nix
@@ -65,6 +65,9 @@
|
||||
# (`nomarchy-theme-sync apply` → `home-manager switch`) works
|
||||
# out of the box — same pinned input as the desktop modules.
|
||||
{ environment.systemPackages = [ home-manager.packages.${system}.home-manager ]; }
|
||||
# System-side theme consumers (Plymouth splash background)
|
||||
# read the same JSON the desktop does.
|
||||
{ nomarchy.system.stateFile = src + "/theme-state.json"; }
|
||||
]
|
||||
++ hardwareModules
|
||||
++ [
|
||||
|
||||
82
modules/home/fuzzel.nix
Normal file
82
modules/home/fuzzel.nix
Normal file
@@ -0,0 +1,82 @@
|
||||
# Fuzzel — launcher, dmenu renderer for the menu system, themed from
|
||||
# theme-state.json like every other surface. Also home of nomarchy-menu,
|
||||
# the dispatcher script the menu roadmap grows out of (first module:
|
||||
# power).
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy;
|
||||
t = cfg.theme;
|
||||
c = t.colors;
|
||||
|
||||
# fuzzel.ini colors are rrggbbaa, no leading #.
|
||||
hexa = color: alpha: "${lib.removePrefix "#" color}${alpha}";
|
||||
|
||||
nomarchy-menu = pkgs.writeShellScriptBin "nomarchy-menu" ''
|
||||
# Nomarchy menu dispatcher — thin presentation layer over
|
||||
# `fuzzel --dmenu`; actions delegate to systemctl/hyprctl/
|
||||
# nomarchy-theme-sync. The icons are nf-md glyphs (any shipped
|
||||
# Nerd Font carries them).
|
||||
case "''${1:-}" in
|
||||
power)
|
||||
choice=$(printf '%s\n' \
|
||||
" Logout" \
|
||||
" Suspend" \
|
||||
" Hibernate" \
|
||||
" Reboot" \
|
||||
" Shutdown" \
|
||||
| fuzzel --dmenu --prompt "power " --lines 5 --width 24) || exit 0
|
||||
case "$choice" in
|
||||
*Logout) hyprctl dispatch exit ;;
|
||||
*Suspend) systemctl suspend ;;
|
||||
*Hibernate) systemctl hibernate ;;
|
||||
*Reboot) systemctl reboot ;;
|
||||
*Shutdown) systemctl poweroff ;;
|
||||
esac ;;
|
||||
*)
|
||||
echo "usage: nomarchy-menu power" >&2
|
||||
exit 64 ;;
|
||||
esac
|
||||
'';
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.fuzzel.enable {
|
||||
home.packages = [ nomarchy-menu ];
|
||||
|
||||
programs.fuzzel = {
|
||||
enable = true;
|
||||
settings = {
|
||||
main = {
|
||||
# UI font first; the mono Nerd Font follows so menu icons
|
||||
# (nf-md glyphs) resolve — fcft falls back per glyph.
|
||||
font = "${t.fonts.ui}:size=${toString (t.fonts.size + 1)}, ${t.fonts.mono}:size=${toString (t.fonts.size + 1)}";
|
||||
terminal = cfg.terminal;
|
||||
layer = "overlay";
|
||||
lines = 12;
|
||||
width = 40;
|
||||
horizontal-pad = 24;
|
||||
vertical-pad = 16;
|
||||
inner-pad = 8;
|
||||
};
|
||||
|
||||
colors = {
|
||||
background = hexa c.base "f2";
|
||||
text = hexa c.text "ff";
|
||||
prompt = hexa c.subtext "ff";
|
||||
placeholder = hexa c.muted "ff";
|
||||
input = hexa c.text "ff";
|
||||
match = hexa c.accent "ff";
|
||||
selection = hexa c.surface "ff";
|
||||
selection-text = hexa c.text "ff";
|
||||
selection-match = hexa c.accent "ff";
|
||||
border = hexa c.accent "ee";
|
||||
};
|
||||
|
||||
border = {
|
||||
width = t.ui.borderSize;
|
||||
radius = t.ui.rounding;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
72
modules/home/idle.nix
Normal file
72
modules/home/idle.nix
Normal file
@@ -0,0 +1,72 @@
|
||||
# hyprlock + hypridle — screen locking and idle management, themed from
|
||||
# theme-state.json. One concern, one file: hypridle drives WHEN (idle
|
||||
# lock, display off, suspend, lock-before-sleep), hyprlock is the
|
||||
# themed lock screen itself (also behind the power menu's Lock entry).
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy;
|
||||
t = cfg.theme;
|
||||
c = t.colors;
|
||||
inherit (config.nomarchy.lib) rgb;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.idle.enable {
|
||||
programs.hyprlock = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general.hide_cursor = true;
|
||||
|
||||
background = [{
|
||||
monitor = "";
|
||||
color = rgb c.base;
|
||||
}];
|
||||
|
||||
input-field = [{
|
||||
monitor = "";
|
||||
size = "300, 50";
|
||||
outline_thickness = t.ui.borderSize;
|
||||
dots_size = 0.25;
|
||||
outer_color = rgb c.accent;
|
||||
inner_color = rgb c.surface;
|
||||
font_color = rgb c.text;
|
||||
check_color = rgb c.warn;
|
||||
fail_color = rgb c.bad;
|
||||
rounding = t.ui.rounding;
|
||||
placeholder_text = "<i>password…</i>";
|
||||
}];
|
||||
|
||||
label = [{
|
||||
monitor = "";
|
||||
text = "$TIME";
|
||||
color = rgb c.text;
|
||||
font_size = 64;
|
||||
font_family = t.fonts.ui;
|
||||
position = "0, 120";
|
||||
halign = "center";
|
||||
valign = "center";
|
||||
}];
|
||||
};
|
||||
};
|
||||
|
||||
services.hypridle = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general = {
|
||||
lock_cmd = "pidof hyprlock || hyprlock";
|
||||
before_sleep_cmd = "loginctl lock-session";
|
||||
after_sleep_cmd = "hyprctl dispatch dpms on";
|
||||
};
|
||||
listener = [
|
||||
{ timeout = 300; on-timeout = "loginctl lock-session"; }
|
||||
{
|
||||
timeout = 600;
|
||||
on-timeout = "hyprctl dispatch dpms off";
|
||||
on-resume = "hyprctl dispatch dpms on";
|
||||
}
|
||||
{ timeout = 1800; on-timeout = "systemctl suspend"; }
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
92
modules/home/swaync.nix
Normal file
92
modules/home/swaync.nix
Normal file
@@ -0,0 +1,92 @@
|
||||
# swaync — notification daemon + control centre, themed from
|
||||
# theme-state.json. Until this shipped, nothing rendered notify-send at
|
||||
# all: the theme-switch progress toasts, the CLI's font warnings and the
|
||||
# live ISO's welcome message were all invisible.
|
||||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy;
|
||||
t = cfg.theme;
|
||||
c = t.colors;
|
||||
r = toString t.ui.rounding;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.swaync.enable {
|
||||
services.swaync = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
positionX = "right";
|
||||
positionY = "top";
|
||||
control-center-margin-top = t.ui.gapsOut;
|
||||
control-center-margin-right = t.ui.gapsOut;
|
||||
control-center-width = 420;
|
||||
notification-window-width = 400;
|
||||
notification-icon-size = 48;
|
||||
timeout = 6;
|
||||
timeout-low = 3;
|
||||
timeout-critical = 0; # critical stays until dismissed
|
||||
};
|
||||
|
||||
style = ''
|
||||
/* Palette baked from theme-state.json */
|
||||
@define-color base ${c.base};
|
||||
@define-color surface ${c.surface};
|
||||
@define-color text ${c.text};
|
||||
@define-color subtext ${c.subtext};
|
||||
@define-color accent ${c.accent};
|
||||
@define-color bad ${c.bad};
|
||||
|
||||
.notification {
|
||||
background: alpha(@base, 0.95);
|
||||
border: ${toString t.ui.borderSize}px solid alpha(@accent, 0.4);
|
||||
border-radius: ${r}px;
|
||||
color: @text;
|
||||
}
|
||||
|
||||
.notification-content .summary {
|
||||
color: @text;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.notification-content .body {
|
||||
color: @subtext;
|
||||
}
|
||||
|
||||
.notification.critical {
|
||||
border-color: @bad;
|
||||
}
|
||||
|
||||
.control-center {
|
||||
background: alpha(@base, 0.95);
|
||||
border: ${toString t.ui.borderSize}px solid alpha(@accent, 0.4);
|
||||
border-radius: ${r}px;
|
||||
color: @text;
|
||||
}
|
||||
|
||||
.control-center .notification-row:focus,
|
||||
.control-center .notification-row:hover {
|
||||
background: alpha(@surface, 0.6);
|
||||
border-radius: ${r}px;
|
||||
}
|
||||
|
||||
.widget-title {
|
||||
color: @text;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.widget-title > button {
|
||||
background: @surface;
|
||||
color: @text;
|
||||
border: none;
|
||||
border-radius: ${r}px;
|
||||
}
|
||||
|
||||
.widget-title > button:hover {
|
||||
background: @accent;
|
||||
color: @base;
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -11,9 +11,15 @@ let
|
||||
cfg = config.nomarchy.system;
|
||||
in
|
||||
{
|
||||
imports = [ ./options.nix ];
|
||||
imports = [ ./options.nix ./plymouth.nix ];
|
||||
|
||||
config = {
|
||||
# The safe half of distro branding: distroName flows into
|
||||
# /etc/os-release PRETTY_NAME, systemd-boot entry titles and the
|
||||
# ISO boot-menu label. distroId stays "nixos" on purpose — it feeds
|
||||
# DEFAULT_HOSTNAME and upstream isNixos checks (see roadmap).
|
||||
system.nixos.distroName = lib.mkDefault "Nomarchy";
|
||||
|
||||
# Unfree allowed distro-wide: pragmatic-desktop territory (claude-code
|
||||
# for the menu's ask-Claude module, vendor drivers, …). The custom
|
||||
# nixpkgs-config type can't carry a nested mkDefault; disagree with
|
||||
@@ -114,6 +120,9 @@ in
|
||||
inter
|
||||
noto-fonts
|
||||
noto-fonts-color-emoji
|
||||
# The legacy-ported waybar identities (summer-day/night) use
|
||||
# FontAwesome codepoints in their layouts and list it in css.
|
||||
font-awesome
|
||||
];
|
||||
fontconfig.defaultFonts = {
|
||||
monospace = lib.mkDefault [ "JetBrainsMono Nerd Font" ];
|
||||
@@ -125,6 +134,31 @@ in
|
||||
# ── Essential packages ───────────────────────────────────────────
|
||||
environment.systemPackages = with pkgs; [
|
||||
nomarchy-theme-sync # provided by overlays.default
|
||||
|
||||
# Friendly wrappers for the two rebuild paths (README §3). Run as
|
||||
# your user: `nix flake update` must NOT run as root (libgit2
|
||||
# refuses the user-owned flake repo) — sudo happens inside, only
|
||||
# for the system switch.
|
||||
(pkgs.writeShellScriptBin "sys-update" ''
|
||||
set -e
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
echo "sys-update: run as your normal user (it sudos the rebuild itself)" >&2
|
||||
exit 1
|
||||
fi
|
||||
flake="''${NOMARCHY_PATH:-$HOME/.nomarchy}"
|
||||
echo "sys-update: updating flake inputs in $flake"
|
||||
nix flake update --flake "$flake"
|
||||
if command -v nixos-rebuild-snap >/dev/null 2>&1; then
|
||||
sudo nixos-rebuild-snap "$@" # BTRFS snapshot first
|
||||
else
|
||||
sudo nixos-rebuild switch --flake "$flake#default" "$@"
|
||||
fi
|
||||
'')
|
||||
(pkgs.writeShellScriptBin "home-update" ''
|
||||
set -e
|
||||
exec home-manager switch --flake "''${NOMARCHY_PATH:-$HOME/.nomarchy}" "$@"
|
||||
'')
|
||||
|
||||
git
|
||||
vim
|
||||
wget
|
||||
@@ -152,6 +186,9 @@ in
|
||||
nixos-rebuild switch --flake /etc/nixos#default "$@"
|
||||
'');
|
||||
|
||||
# Don't let boot entries fill the ESP over the years.
|
||||
boot.loader.systemd-boot.configurationLimit = lib.mkDefault 10;
|
||||
|
||||
# ── Nix itself ───────────────────────────────────────────────────
|
||||
nix = {
|
||||
settings = {
|
||||
|
||||
@@ -21,6 +21,22 @@
|
||||
'';
|
||||
};
|
||||
|
||||
plymouth.enable = lib.mkEnableOption ''
|
||||
the Nomarchy Plymouth boot splash (logo + progress + LUKS prompt),
|
||||
background-tinted from theme-state.json via nomarchy.system.stateFile.
|
||||
Recolors on system rebuilds — theme switches don't touch the initrd'' // { default = true; };
|
||||
|
||||
stateFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = lib.literalExpression "./theme-state.json";
|
||||
description = ''
|
||||
theme-state.json for the system-side consumers (currently the
|
||||
Plymouth splash background). lib.mkFlake wires it automatically
|
||||
from your flake; null falls back to the Tokyo Night base color.
|
||||
'';
|
||||
};
|
||||
|
||||
audio.enable = lib.mkEnableOption "the Pipewire audio stack" // { default = true; };
|
||||
bluetooth.enable = lib.mkEnableOption "Bluetooth support with blueman" // { default = true; };
|
||||
|
||||
|
||||
87
modules/nixos/plymouth.nix
Normal file
87
modules/nixos/plymouth.nix
Normal file
@@ -0,0 +1,87 @@
|
||||
# Plymouth boot splash — Nomarchy-branded (ported from the legacy
|
||||
# iteration), background tinted from the same theme-state.json that
|
||||
# drives the desktop (nomarchy.system.stateFile, wired automatically by
|
||||
# lib.mkFlake). One caveat by design: theme switches are Home
|
||||
# Manager-only and never touch the initrd, so the splash follows the
|
||||
# theme as of the last SYSTEM rebuild (`sys-update`), not the last
|
||||
# `nomarchy-theme-sync apply`.
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.nomarchy.system;
|
||||
|
||||
state =
|
||||
if cfg.stateFile != null
|
||||
then builtins.fromJSON (builtins.readFile cfg.stateFile)
|
||||
else { };
|
||||
base = lib.removePrefix "#" ((state.colors or { }).base or "#1a1b26");
|
||||
|
||||
# Plymouth's Window.SetBackgroundTopColor takes three floats in
|
||||
# 0.0–1.0; the .plymouth metadata's ConsoleLogBackgroundColor takes a
|
||||
# 0xRRGGBB hex. Nix has no float math: multiply, integer-divide, pad.
|
||||
byteToFloat = n:
|
||||
let
|
||||
thousandths = (n * 1000) / 255;
|
||||
s = toString thousandths;
|
||||
padded =
|
||||
if lib.stringLength s == 1 then "00${s}"
|
||||
else if lib.stringLength s == 2 then "0${s}"
|
||||
else s;
|
||||
in "0.${padded}";
|
||||
channel = off: byteToFloat (lib.fromHexString (lib.substring off 2 base));
|
||||
|
||||
nomarchy-plymouth = pkgs.stdenv.mkDerivation {
|
||||
pname = "nomarchy-plymouth";
|
||||
version = "1.0";
|
||||
|
||||
src = ./plymouth;
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/share/plymouth/themes/nomarchy
|
||||
cp * $out/share/plymouth/themes/nomarchy/
|
||||
|
||||
# Point the .plymouth metadata into the store
|
||||
sed -i "s|/usr/share/plymouth/themes/nomarchy|$out/share/plymouth/themes/nomarchy|g" \
|
||||
$out/share/plymouth/themes/nomarchy/nomarchy.plymouth
|
||||
|
||||
# Bake the theme's base color: RGB floats for the script's
|
||||
# Window.SetBackground* calls, 0xRRGGBB for ConsoleLogBackground.
|
||||
sed -i \
|
||||
-e 's|@BG_R@|${channel 0}|g' \
|
||||
-e 's|@BG_G@|${channel 2}|g' \
|
||||
-e 's|@BG_B@|${channel 4}|g' \
|
||||
$out/share/plymouth/themes/nomarchy/nomarchy.script
|
||||
sed -i 's|@BG_HEX@|${base}|g' \
|
||||
$out/share/plymouth/themes/nomarchy/nomarchy.plymouth
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.plymouth.enable {
|
||||
# Plymouth wants the systemd initrd (also what applies the keyboard
|
||||
# layout to the LUKS prompt) and a quiet console around it.
|
||||
boot.initrd.systemd.enable = lib.mkDefault true;
|
||||
boot.initrd.verbose = lib.mkDefault false;
|
||||
console.earlySetup = lib.mkDefault true;
|
||||
boot.consoleLogLevel = lib.mkDefault 0;
|
||||
|
||||
boot.plymouth = {
|
||||
enable = lib.mkDefault true;
|
||||
themePackages = lib.mkDefault [ nomarchy-plymouth ];
|
||||
theme = lib.mkDefault "nomarchy";
|
||||
};
|
||||
|
||||
# Not mkDefault: kernelParams is a list other modules add to at
|
||||
# normal priority — a mkDefault def would be dropped entirely,
|
||||
# losing the quiet/splash boot. These merge with the rest.
|
||||
boot.kernelParams = [
|
||||
"quiet"
|
||||
"splash"
|
||||
"loglevel=3"
|
||||
"rd.systemd.show_status=false"
|
||||
"rd.udev.log_level=3"
|
||||
"udev.log_priority=3"
|
||||
"boot.shell_on_fail"
|
||||
];
|
||||
};
|
||||
}
|
||||
BIN
modules/nixos/plymouth/bullet.png
Normal file
BIN
modules/nixos/plymouth/bullet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 293 B |
BIN
modules/nixos/plymouth/entry.png
Normal file
BIN
modules/nixos/plymouth/entry.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 694 B |
BIN
modules/nixos/plymouth/lock.png
Normal file
BIN
modules/nixos/plymouth/lock.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
modules/nixos/plymouth/logo.png
Normal file
BIN
modules/nixos/plymouth/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
11
modules/nixos/plymouth/nomarchy.plymouth
Normal file
11
modules/nixos/plymouth/nomarchy.plymouth
Normal file
@@ -0,0 +1,11 @@
|
||||
[Plymouth Theme]
|
||||
Name=Nomarchy
|
||||
Description=Nomarchy splash screen.
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/nomarchy
|
||||
ScriptFile=/usr/share/plymouth/themes/nomarchy/nomarchy.script
|
||||
ConsoleLogBackgroundColor=0x@BG_HEX@
|
||||
MonospaceFont=Cantarell 11
|
||||
Font=Cantarell 11
|
||||
271
modules/nixos/plymouth/nomarchy.script
Normal file
271
modules/nixos/plymouth/nomarchy.script
Normal file
@@ -0,0 +1,271 @@
|
||||
# Nomarchy Plymouth Theme Script
|
||||
|
||||
Window.SetBackgroundTopColor(@BG_R@, @BG_G@, @BG_B@);
|
||||
Window.SetBackgroundBottomColor(@BG_R@, @BG_G@, @BG_B@);
|
||||
|
||||
logo.image = Image("logo.png");
|
||||
|
||||
# Calculate scale factor to make logo ~15% of screen height
|
||||
logo_scale_factor = (Window.GetHeight() * 0.15) / logo.image.GetHeight();
|
||||
logo_width = logo.image.GetWidth() * logo_scale_factor;
|
||||
logo_height = logo.image.GetHeight() * logo_scale_factor;
|
||||
logo.image = logo.image.Scale(logo_width, logo_height);
|
||||
|
||||
logo.sprite = Sprite(logo.image);
|
||||
logo.sprite.SetX (Window.GetWidth() / 2 - logo.image.GetWidth() / 2);
|
||||
logo.sprite.SetY (Window.GetHeight() / 2 - logo.image.GetHeight() / 2);
|
||||
logo.sprite.SetOpacity (1);
|
||||
|
||||
# Use these to adjust the progress bar timing
|
||||
global.fake_progress_limit = 0.7; # Target percentage for fake progress (0.0 to 1.0)
|
||||
global.fake_progress_duration = 15.0; # Duration in seconds to reach limit
|
||||
|
||||
# Progress bar animation variables
|
||||
global.fake_progress = 0.0;
|
||||
global.real_progress = 0.0;
|
||||
global.fake_progress_active = 0; # 0 / 1 boolean
|
||||
global.animation_frame = 0;
|
||||
global.fake_progress_start_time = 0; # Track when fake progress started
|
||||
global.password_shown = 0; # Track if password dialog has been shown
|
||||
global.max_progress = 0.0; # Track the maximum progress reached to prevent backwards movement
|
||||
|
||||
fun refresh_callback ()
|
||||
{
|
||||
global.animation_frame++;
|
||||
|
||||
# Animate fake progress to limit over time with easing
|
||||
if (global.fake_progress_active == 1)
|
||||
{
|
||||
# Calculate elapsed time since start
|
||||
elapsed_time = global.animation_frame / 50.0; # Convert frames to seconds (50 FPS)
|
||||
|
||||
# Calculate linear progress ratio (0 to 1) based on time
|
||||
time_ratio = elapsed_time / global.fake_progress_duration;
|
||||
if (time_ratio > 1.0)
|
||||
time_ratio = 1.0;
|
||||
|
||||
# Apply easing curve: ease-out quadratic
|
||||
# Formula: 1 - (1 - x)^2
|
||||
eased_ratio = 1 - ((1 - time_ratio) * (1 - time_ratio));
|
||||
|
||||
# Calculate fake progress based on eased ratio
|
||||
global.fake_progress = eased_ratio * global.fake_progress_limit;
|
||||
|
||||
# Update progress bar with fake progress
|
||||
update_progress_bar(global.fake_progress);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Plymouth.SetRefreshFunction (refresh_callback);
|
||||
|
||||
#----------------------------------------- Helper Functions --------------------------------
|
||||
|
||||
fun update_progress_bar(progress)
|
||||
{
|
||||
# Only update if progress is moving forward
|
||||
if (progress > global.max_progress)
|
||||
{
|
||||
global.max_progress = progress;
|
||||
width = Math.Int(progress_bar.original_image.GetWidth() * progress);
|
||||
if (width < 1) width = 1; # Ensure minimum width of 1 pixel
|
||||
|
||||
progress_bar.image = progress_bar.original_image.Scale(width, progress_bar.original_image.GetHeight());
|
||||
progress_bar.sprite.SetImage(progress_bar.image);
|
||||
}
|
||||
}
|
||||
|
||||
fun show_progress_bar()
|
||||
{
|
||||
progress_box.sprite.SetOpacity(1);
|
||||
progress_bar.sprite.SetOpacity(1);
|
||||
}
|
||||
|
||||
fun hide_progress_bar()
|
||||
{
|
||||
progress_box.sprite.SetOpacity(0);
|
||||
progress_bar.sprite.SetOpacity(0);
|
||||
}
|
||||
|
||||
fun show_password_dialog()
|
||||
{
|
||||
lock.sprite.SetOpacity(1);
|
||||
entry.sprite.SetOpacity(1);
|
||||
}
|
||||
|
||||
fun hide_password_dialog()
|
||||
{
|
||||
lock.sprite.SetOpacity(0);
|
||||
entry.sprite.SetOpacity(0);
|
||||
for (index = 0; bullet.sprites[index]; index++)
|
||||
bullet.sprites[index].SetOpacity(0);
|
||||
}
|
||||
|
||||
fun start_fake_progress()
|
||||
{
|
||||
# Don't reset if we already have progress
|
||||
if (global.max_progress == 0.0)
|
||||
{
|
||||
global.fake_progress = 0.0;
|
||||
global.real_progress = 0.0;
|
||||
update_progress_bar(0.0);
|
||||
}
|
||||
global.fake_progress_active = 1;
|
||||
global.animation_frame = 0;
|
||||
}
|
||||
|
||||
fun stop_fake_progress()
|
||||
{
|
||||
global.fake_progress_active = 0;
|
||||
}
|
||||
|
||||
#----------------------------------------- Dialogue --------------------------------
|
||||
|
||||
lock.image = Image("lock.png");
|
||||
entry.image = Image("entry.png");
|
||||
bullet.image = Image("bullet.png");
|
||||
|
||||
entry.sprite = Sprite(entry.image);
|
||||
entry.x = Window.GetWidth()/2 - entry.image.GetWidth() / 2;
|
||||
entry.y = logo.sprite.GetY() + logo.image.GetHeight() + 40;
|
||||
entry.sprite.SetPosition(entry.x, entry.y, 10001);
|
||||
entry.sprite.SetOpacity(0);
|
||||
|
||||
# Scale lock to be slightly shorter than entry field height
|
||||
# Original lock is 84x96, entry height determines scale
|
||||
lock_height = entry.image.GetHeight() * 0.8;
|
||||
lock_scale = lock_height / 96;
|
||||
lock_width = 84 * lock_scale;
|
||||
|
||||
scaled_lock = lock.image.Scale(lock_width, lock_height);
|
||||
lock.sprite = Sprite(scaled_lock);
|
||||
lock.x = entry.x - lock_width - 15;
|
||||
lock.y = entry.y + entry.image.GetHeight()/2 - lock_height/2;
|
||||
lock.sprite.SetPosition(lock.x, lock.y, 10001);
|
||||
lock.sprite.SetOpacity(0);
|
||||
|
||||
# Bullet array
|
||||
bullet.sprites = [];
|
||||
|
||||
fun display_normal_callback ()
|
||||
{
|
||||
hide_password_dialog();
|
||||
|
||||
# Get current mode
|
||||
mode = Plymouth.GetMode();
|
||||
|
||||
# Only show progress bar for boot and resume modes
|
||||
if (mode == "boot" || mode == "resume")
|
||||
{
|
||||
show_progress_bar();
|
||||
start_fake_progress();
|
||||
}
|
||||
}
|
||||
|
||||
fun display_password_callback (prompt, bullets)
|
||||
{
|
||||
global.password_shown = 1; # Mark that password dialog has been shown
|
||||
|
||||
# Reset progress when password dialog appears
|
||||
stop_fake_progress();
|
||||
hide_progress_bar();
|
||||
global.max_progress = 0.0;
|
||||
global.fake_progress = 0.0;
|
||||
global.real_progress = 0.0;
|
||||
show_password_dialog();
|
||||
|
||||
# Clear all bullets first
|
||||
for (index = 0; bullet.sprites[index]; index++)
|
||||
bullet.sprites[index].SetOpacity(0);
|
||||
|
||||
# Create and show bullets for current password (max 21)
|
||||
max_bullets = 21;
|
||||
bullets_to_show = bullets;
|
||||
if (bullets_to_show > max_bullets)
|
||||
bullets_to_show = max_bullets;
|
||||
|
||||
for (index = 0; index < bullets_to_show; index++)
|
||||
{
|
||||
if (!bullet.sprites[index])
|
||||
{
|
||||
# Scale bullet image to 7x7 pixels
|
||||
scaled_bullet = bullet.image.Scale(7, 7);
|
||||
bullet.sprites[index] = Sprite(scaled_bullet);
|
||||
bullet.x = entry.x + 20 + index * (7 + 5);
|
||||
bullet.y = entry.y + entry.image.GetHeight() / 2 - 3.5;
|
||||
bullet.sprites[index].SetPosition(bullet.x, bullet.y, 10002);
|
||||
}
|
||||
bullet.sprites[index].SetOpacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
Plymouth.SetDisplayNormalFunction(display_normal_callback);
|
||||
Plymouth.SetDisplayPasswordFunction(display_password_callback);
|
||||
|
||||
#----------------------------------------- Progress Bar --------------------------------
|
||||
|
||||
progress_box.image = Image("progress_box.png");
|
||||
progress_box.sprite = Sprite(progress_box.image);
|
||||
|
||||
progress_box.x = Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2;
|
||||
progress_box.y = entry.y + entry.image.GetHeight() / 2 - progress_box.image.GetHeight() / 2;
|
||||
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);
|
||||
progress_box.sprite.SetOpacity(0);
|
||||
|
||||
progress_bar.original_image = Image("progress_bar.png");
|
||||
progress_bar.sprite = Sprite();
|
||||
progress_bar.image = progress_bar.original_image.Scale(1, progress_bar.original_image.GetHeight());
|
||||
|
||||
progress_bar.x = Window.GetWidth() / 2 - progress_bar.original_image.GetWidth() / 2;
|
||||
progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
|
||||
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);
|
||||
progress_bar.sprite.SetOpacity(0);
|
||||
|
||||
fun progress_callback (duration, progress)
|
||||
{
|
||||
global.real_progress = progress;
|
||||
|
||||
# If real progress is above limit, stop fake progress and use real progress
|
||||
if (progress > global.fake_progress_limit)
|
||||
{
|
||||
stop_fake_progress();
|
||||
update_progress_bar(progress);
|
||||
}
|
||||
}
|
||||
|
||||
Plymouth.SetBootProgressFunction(progress_callback);
|
||||
|
||||
#----------------------------------------- Quit --------------------------------
|
||||
|
||||
fun quit_callback ()
|
||||
{
|
||||
logo.sprite.SetOpacity (1);
|
||||
}
|
||||
|
||||
Plymouth.SetQuitFunction(quit_callback);
|
||||
|
||||
#----------------------------------------- Message --------------------------------
|
||||
|
||||
message_sprite = Sprite();
|
||||
message_sprite.SetPosition(10, 10, 10000);
|
||||
|
||||
fun display_message_callback (text)
|
||||
{
|
||||
my_image = Image.Text(text, 1, 1, 1);
|
||||
message_sprite.SetImage(my_image);
|
||||
}
|
||||
|
||||
fun hide_message_callback (text)
|
||||
{
|
||||
message_sprite.SetOpacity(0);
|
||||
}
|
||||
|
||||
Plymouth.SetDisplayMessageFunction (display_message_callback);
|
||||
Plymouth.SetHideMessageFunction (hide_message_callback);
|
||||
|
||||
# Initialize progress bar immediately for normal boots
|
||||
if (Plymouth.GetMode() == "boot" || Plymouth.GetMode() == "resume")
|
||||
{
|
||||
show_progress_bar();
|
||||
start_fake_progress();
|
||||
}
|
||||
BIN
modules/nixos/plymouth/progress_bar.png
Normal file
BIN
modules/nixos/plymouth/progress_bar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 314 B |
BIN
modules/nixos/plymouth/progress_box.png
Normal file
BIN
modules/nixos/plymouth/progress_box.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 314 B |
92
themes/summer-day/waybar.jsonc
Normal file
92
themes/summer-day/waybar.jsonc
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"margin-top": 0,
|
||||
"margin-left": 120,
|
||||
"margin-bottom": 0,
|
||||
"margin-right": 120,
|
||||
"height": 60,
|
||||
"layer": "top",
|
||||
"position": "top",
|
||||
"spacing": 15,
|
||||
"reload_style_on_change": true,
|
||||
|
||||
"modules-left": ["custom/launcher", "clock", "clock#date"],
|
||||
"modules-center": ["hyprland/workspaces"],
|
||||
"modules-right": ["pulseaudio", "network", "battery", "tray", "custom/powermenu"],
|
||||
|
||||
"hyprland/workspaces": {
|
||||
"disable-scroll": true,
|
||||
"all-outputs": true,
|
||||
"on-click": "activate",
|
||||
"on-scroll-up": "hyprctl dispatch workspace e+1",
|
||||
"on-scroll-down": "hyprctl dispatch workspace e-1",
|
||||
"persistent-workspaces": {
|
||||
"1": [], "2": [], "3": [], "4": [], "5": [],
|
||||
"6": [], "7": [], "8": [], "9": [], "10": []
|
||||
}
|
||||
},
|
||||
|
||||
"custom/launcher": {
|
||||
"interval": "once",
|
||||
"format": "",
|
||||
"on-click": "nomarchy-menu",
|
||||
"tooltip-format": "Nomarchy menu"
|
||||
},
|
||||
|
||||
"pulseaudio": {
|
||||
"format": "{icon} {volume}%",
|
||||
"format-bluetooth": "{icon} {volume}%",
|
||||
"format-bluetooth-muted": " muted",
|
||||
"format-muted": " muted",
|
||||
"format-icons": {
|
||||
"headphone": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"phone": "",
|
||||
"portable": "",
|
||||
"car": "",
|
||||
"default": ["", "", ""]
|
||||
},
|
||||
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
|
||||
"tooltip-format": "Playing at {volume}%"
|
||||
},
|
||||
|
||||
"network": {
|
||||
"format-wifi": " {signalStrength}%",
|
||||
"format-ethernet": "",
|
||||
"format-disconnected": "",
|
||||
"on-click": "sh -c '$TERMINAL -e nmtui'",
|
||||
"tooltip-format": "{ipaddr} via {gwaddr}"
|
||||
},
|
||||
|
||||
"battery": {
|
||||
"interval": 60,
|
||||
"states": { "warning": 30, "critical": 15 },
|
||||
"max-length": 10,
|
||||
"format": "{icon} {capacity}%",
|
||||
"format-charging": " {capacity}%",
|
||||
"format-plugged": " {capacity}%",
|
||||
"format-full": " 100%",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
"on-click": "nomarchy-menu power",
|
||||
"tooltip-format": "Battery status"
|
||||
},
|
||||
|
||||
"clock": {
|
||||
"format": " {:%H:%M}",
|
||||
"tooltip": true,
|
||||
"tooltip-format": "<tt><small>{calendar}</small></tt>"
|
||||
},
|
||||
|
||||
"clock#date": {
|
||||
"format": " {:%A, %B %d, %Y}",
|
||||
"tooltip": false
|
||||
},
|
||||
|
||||
"tray": { "spacing": 8 },
|
||||
|
||||
"custom/powermenu": {
|
||||
"format": "",
|
||||
"on-click": "nomarchy-menu power",
|
||||
"tooltip": false
|
||||
}
|
||||
}
|
||||
99
themes/summer-night/waybar.jsonc
Normal file
99
themes/summer-night/waybar.jsonc
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"margin-top": 0,
|
||||
"margin-left": 120,
|
||||
"margin-bottom": 0,
|
||||
"margin-right": 120,
|
||||
"height": 61,
|
||||
"layer": "top",
|
||||
"position": "top",
|
||||
"spacing": 15,
|
||||
"reload_style_on_change": true,
|
||||
|
||||
"modules-left": ["custom/nomarchy", "clock", "clock#date"],
|
||||
"modules-center": ["hyprland/workspaces"],
|
||||
"modules-right": ["idle_inhibitor", "pulseaudio", "network", "battery", "tray", "custom/powermenu"],
|
||||
|
||||
"hyprland/workspaces": {
|
||||
"disable-scroll": true,
|
||||
"all-outputs": true,
|
||||
"on-click": "activate",
|
||||
"on-scroll-up": "hyprctl dispatch workspace r+1",
|
||||
"on-scroll-down": "hyprctl dispatch workspace r-1",
|
||||
"persistent-workspaces": {
|
||||
"1": [], "2": [], "3": [], "4": [], "5": [],
|
||||
"6": [], "7": [], "8": [], "9": [], "10": []
|
||||
}
|
||||
},
|
||||
|
||||
"custom/nomarchy": {
|
||||
"interval": "once",
|
||||
"format": "",
|
||||
"on-click": "nomarchy-menu",
|
||||
"tooltip-format": "Nomarchy menu"
|
||||
},
|
||||
|
||||
"idle_inhibitor": {
|
||||
"format": "{icon}",
|
||||
"format-icons": {
|
||||
"activated": " ",
|
||||
"deactivated": " "
|
||||
}
|
||||
},
|
||||
|
||||
"pulseaudio": {
|
||||
"scroll-step": 5,
|
||||
"format": "{icon} {volume}%",
|
||||
"format-bluetooth": "{icon} {volume}%",
|
||||
"format-bluetooth-muted": " {icon}",
|
||||
"format-muted": " muted",
|
||||
"format-icons": {
|
||||
"headphone": "",
|
||||
"hands-free": "",
|
||||
"headset": "",
|
||||
"phone": "",
|
||||
"portable": "",
|
||||
"car": "",
|
||||
"default": ["", "", ""]
|
||||
},
|
||||
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
|
||||
"tooltip-format": "{desc} | {volume}%"
|
||||
},
|
||||
|
||||
"network": {
|
||||
"format-wifi": " {essid}",
|
||||
"format-ethernet": "",
|
||||
"format-disconnected": "",
|
||||
"on-click": "sh -c '$TERMINAL -e nmtui'",
|
||||
"tooltip-format": "{ipaddr} via {gwaddr}"
|
||||
},
|
||||
|
||||
"battery": {
|
||||
"interval": 30,
|
||||
"states": { "warning": 25, "critical": 10 },
|
||||
"format": "{icon} {capacity}%",
|
||||
"format-charging": " {capacity}%",
|
||||
"format-plugged": " {capacity}%",
|
||||
"format-icons": ["", "", "", "", ""],
|
||||
"on-click": "nomarchy-menu power",
|
||||
"tooltip-format": "Battery status"
|
||||
},
|
||||
|
||||
"clock": {
|
||||
"format": " {:%H:%M}",
|
||||
"tooltip": true,
|
||||
"tooltip-format": "<tt><small>{calendar}</small></tt>"
|
||||
},
|
||||
|
||||
"clock#date": {
|
||||
"format": " {:%A, %B %d, %Y}",
|
||||
"tooltip": false
|
||||
},
|
||||
|
||||
"tray": { "spacing": 8 },
|
||||
|
||||
"custom/powermenu": {
|
||||
"format": "",
|
||||
"on-click": "nomarchy-menu power",
|
||||
"tooltip": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user