diff --git a/flake.nix b/flake.nix index 8df64cd..825a676 100644 --- a/flake.nix +++ b/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 diff --git a/hosts/live.nix b/hosts/live.nix index d3f0c8d..ac769a0 100644 --- a/hosts/live.nix +++ b/hosts/live.nix @@ -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. diff --git a/lib.nix b/lib.nix index 07e442d..ff9746c 100644 --- a/lib.nix +++ b/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 ++ [ diff --git a/modules/home/fuzzel.nix b/modules/home/fuzzel.nix new file mode 100644 index 0000000..b0f126b --- /dev/null +++ b/modules/home/fuzzel.nix @@ -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; + }; + }; + }; + }; +} diff --git a/modules/home/idle.nix b/modules/home/idle.nix new file mode 100644 index 0000000..db4b173 --- /dev/null +++ b/modules/home/idle.nix @@ -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 = "password…"; + }]; + + 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"; } + ]; + }; + }; + }; +} diff --git a/modules/home/swaync.nix b/modules/home/swaync.nix new file mode 100644 index 0000000..ed2cba9 --- /dev/null +++ b/modules/home/swaync.nix @@ -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; + } + ''; + }; + }; +} diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix index 4ef0cda..4beaa52 100644 --- a/modules/nixos/default.nix +++ b/modules/nixos/default.nix @@ -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 = { diff --git a/modules/nixos/options.nix b/modules/nixos/options.nix index 2b93139..9ed548e 100644 --- a/modules/nixos/options.nix +++ b/modules/nixos/options.nix @@ -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; }; diff --git a/modules/nixos/plymouth.nix b/modules/nixos/plymouth.nix new file mode 100644 index 0000000..36b9161 --- /dev/null +++ b/modules/nixos/plymouth.nix @@ -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" + ]; + }; +} diff --git a/modules/nixos/plymouth/bullet.png b/modules/nixos/plymouth/bullet.png new file mode 100644 index 0000000..62249b3 Binary files /dev/null and b/modules/nixos/plymouth/bullet.png differ diff --git a/modules/nixos/plymouth/entry.png b/modules/nixos/plymouth/entry.png new file mode 100644 index 0000000..5c78917 Binary files /dev/null and b/modules/nixos/plymouth/entry.png differ diff --git a/modules/nixos/plymouth/lock.png b/modules/nixos/plymouth/lock.png new file mode 100644 index 0000000..3046de1 Binary files /dev/null and b/modules/nixos/plymouth/lock.png differ diff --git a/modules/nixos/plymouth/logo.png b/modules/nixos/plymouth/logo.png new file mode 100644 index 0000000..dbcde56 Binary files /dev/null and b/modules/nixos/plymouth/logo.png differ diff --git a/modules/nixos/plymouth/nomarchy.plymouth b/modules/nixos/plymouth/nomarchy.plymouth new file mode 100644 index 0000000..4ade91c --- /dev/null +++ b/modules/nixos/plymouth/nomarchy.plymouth @@ -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 diff --git a/modules/nixos/plymouth/nomarchy.script b/modules/nixos/plymouth/nomarchy.script new file mode 100644 index 0000000..216dac9 --- /dev/null +++ b/modules/nixos/plymouth/nomarchy.script @@ -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(); + } diff --git a/modules/nixos/plymouth/progress_bar.png b/modules/nixos/plymouth/progress_bar.png new file mode 100644 index 0000000..dbb9fd7 Binary files /dev/null and b/modules/nixos/plymouth/progress_bar.png differ diff --git a/modules/nixos/plymouth/progress_box.png b/modules/nixos/plymouth/progress_box.png new file mode 100644 index 0000000..6a263f2 Binary files /dev/null and b/modules/nixos/plymouth/progress_box.png differ diff --git a/themes/summer-day/waybar.jsonc b/themes/summer-day/waybar.jsonc new file mode 100644 index 0000000..a45b8f5 --- /dev/null +++ b/themes/summer-day/waybar.jsonc @@ -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": "{calendar}" + }, + + "clock#date": { + "format": " {:%A, %B %d, %Y}", + "tooltip": false + }, + + "tray": { "spacing": 8 }, + + "custom/powermenu": { + "format": "", + "on-click": "nomarchy-menu power", + "tooltip": false + } +} diff --git a/themes/summer-night/waybar.jsonc b/themes/summer-night/waybar.jsonc new file mode 100644 index 0000000..e5a1430 --- /dev/null +++ b/themes/summer-night/waybar.jsonc @@ -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": "{calendar}" + }, + + "clock#date": { + "format": " {:%A, %B %d, %Y}", + "tooltip": false + }, + + "tray": { "spacing": 8 }, + + "custom/powermenu": { + "format": "", + "on-click": "nomarchy-menu power", + "tooltip": false + } +}