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:
Bernardo Magri
2026-06-13 07:37:32 +01:00
parent 1ba1eeed64
commit eda8461304
19 changed files with 906 additions and 1 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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
View 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
View 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
View 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;
}
'';
};
};
}

View File

@@ -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 = {

View File

@@ -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; };

View 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.01.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"
];
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View 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

View 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();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View 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
}
}

View 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
}
}