From 47c0c9c71920dcb7cc670def44577f2a0d93b42d Mon Sep 17 00:00:00 2001 From: Anish Govind Date: Mon, 31 Aug 2026 21:20:26 -0700 Subject: [PATCH] updated quickshell --- README.md | 37 +++++ home/default.nix | 1 + home/foot.nix | 49 +++--- home/hypr/default.nix | 52 ++++++- home/hypr/hypridle.nix | 8 +- home/hypr/hyprland.nix | 139 +++++++++++------- home/hypr/hyprlock.nix | 72 ++++++--- home/hypr/quickshell/GlobalState.qml | 82 +++++++---- .../quickshell/components/AppMenuDropdown.qml | 18 +-- home/hypr/quickshell/components/AppleMenu.qml | 2 +- home/hypr/quickshell/components/Dock.qml | 2 +- home/hypr/quickshell/components/SidePanel.qml | 7 +- home/hypr/quickshell/components/Spotlight.qml | 4 +- home/hypr/quickshell/components/SystemHUD.qml | 50 ++++--- home/hypr/quickshell/components/TopStrip.qml | 116 ++++++++++++--- home/hypr/wallpaper.svg | 53 +++++++ home/rofi.nix | 28 ++++ home/rofi/rofi-scripts.sh | 21 +++ home/rofi/volt.rasi | 135 +++++++++++++++++ home/scripts/herdr-picker.sh | 7 +- home/starship/starship.toml | 36 ++--- home/zsh.nix | 5 +- hosts/hades/default.nix | 20 +++ modules/graphical/default.nix | 29 ++-- modules/hardware/nvidia.nix | 9 ++ 25 files changed, 763 insertions(+), 219 deletions(-) create mode 100644 home/hypr/wallpaper.svg create mode 100644 home/rofi.nix create mode 100644 home/rofi/rofi-scripts.sh create mode 100644 home/rofi/volt.rasi diff --git a/README.md b/README.md index d8c503b..3b9b706 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# NixOS Configs + +Reproducible multi-host NixOS + Hyprland ("Silicon Volt") configuration. + +## Install (new machine) + ```bash # Setup disks nix run --experimental-features "nix-common flakes" github:nix-community/disko -- --mode disko --flake .#hades @@ -8,3 +14,34 @@ nixos-install --flake .#hades # set password nixos-enter --root /mnt -- passwd anish ``` + +## Rebuild (existing machine) + +```bash +rebuild switch # zsh helper: sudo nixos-rebuild switch --flake ~/NixOS-Configs# +``` + +## Adding a new host (e.g. a laptop) + +1. Create `hosts//` with `default.nix`, `hardware-configuration.nix`, and + (optionally) `disko.nix`; import the shared modules (`modules/core`, + `modules/graphical`, `modules/apps`). +2. Register it in `flake.nix` under `nixosConfigurations`. +3. Declare host-specific displays in `hosts//default.nix`: + +```nix +home-manager.users.anish.volt.monitors = [ + { + output = "eDP-1"; # or "desc:" + mode = "2880x1800@120Hz"; # or "preferred" + position = "auto"; + scale = 2; + vrr = null; # optional per-monitor VRR + } + { output = ""; mode = "preferred"; position = "auto"; scale = "auto"; } # fallback +]; +``` + +The `volt.monitors` default (any output @ preferred) keeps every machine working +out of the box; hosts override only what they need. Hosts without an NVIDIA GPU +simply don't import `modules/hardware/nvidia.nix` — all vendor env vars live there. diff --git a/home/default.nix b/home/default.nix index 746fee1..4501f52 100644 --- a/home/default.nix +++ b/home/default.nix @@ -7,6 +7,7 @@ ./brave.nix ./zsh.nix ./nvim + ./rofi.nix ./pi.nix ]; diff --git a/home/foot.nix b/home/foot.nix index 8669de2..fffa5eb 100644 --- a/home/foot.nix +++ b/home/foot.nix @@ -33,6 +33,7 @@ in style = "beam"; blink = "yes"; beam-thickness = 1.5; + color = "0e0e0e cffc00"; }; mouse = { @@ -40,34 +41,34 @@ in }; colors = { - alpha = 0.90; - background = "18181a"; - foreground = "f2f2f7"; + alpha = 0.92; + background = "131313"; + foreground = "e5e2e1"; - # macOS Catalina Apple System Dark Palette - regular0 = "1c1c1e"; # Black / Slate - regular1 = "ff453a"; # Apple Red - regular2 = "30d158"; # Apple Green - regular3 = "ffd60a"; # Apple Yellow - regular4 = "0a84ff"; # Apple Blue - regular5 = "bf5af2"; # Apple Purple - regular6 = "64d2ff"; # Apple Cyan - regular7 = "f2f2f7"; # Apple Light Gray + # Silicon Volt palette (anish.govind.cc) + regular0 = "1c1b1b"; # Glass Black + regular1 = "ff7a59"; # Coral Red + regular2 = "cffc00"; # Electric Lime (green) + regular3 = "e3ff66"; # Volt Light (yellow) + regular4 = "00bfff"; # Sky Blue + regular5 = "c6c6c7"; # Neutral Gray (magenta) + regular6 = "66d9ff"; # Light Cyan + regular7 = "e5e2e1"; # Light Gray # Bright Colors - bright0 = "636366"; # Gray - bright1 = "ff6961"; # Bright Red - bright2 = "34c759"; # Bright Green - bright3 = "ffe066"; # Bright Yellow - bright4 = "409cff"; # Bright Blue - bright5 = "c774f4"; # Bright Purple - bright6 = "70d7ff"; # Bright Cyan - bright7 = "ffffff"; # Pure White + bright0 = "5c5a59"; # Dim Gray + bright1 = "ff9a82"; # Bright Coral + bright2 = "dfff66"; # Bright Lime + bright3 = "f0ff8c"; # Bright Volt + bright4 = "66d9ff"; # Bright Blue + bright5 = "e0e0e1"; # Bright Gray + bright6 = "99e6ff"; # Bright Cyan + bright7 = "ffffff"; # Pure White - # Selection & URLs - selection-foreground = "ffffff"; - selection-background = "0058d0"; # Apple selection blue - urls = "0a84ff"; + # Selection & URLs - volt lime selection like the site + selection-foreground = "0e0e0e"; + selection-background = "cffc00"; + urls = "00bfff"; }; }; }; diff --git a/home/hypr/default.nix b/home/hypr/default.nix index e0475d8..ea4ecbd 100644 --- a/home/hypr/default.nix +++ b/home/hypr/default.nix @@ -1,5 +1,12 @@ -{ pkgs, ... }: +{ pkgs, lib, config, ... }: +let + # Deterministic Silicon Volt wallpaper: rendered from SVG at build time, + # so the same source always produces a byte-identical image. + wallpaper = pkgs.runCommand "silicon-volt-wallpaper.png" { } '' + ${pkgs.librsvg}/bin/rsvg-convert -w 3840 -h 2160 ${./wallpaper.svg} > $out + ''; +in { imports = [ ./hyprland.nix @@ -15,10 +22,51 @@ wl-clipboard cliphist brightnessctl + jq # Desktop Environment / Hyprland ecosystem hyprpaper - rofi hyprpolkitagent ]; + + # Build-time rendered wallpaper + xdg.dataFile."backgrounds/silicon-volt.png".source = wallpaper; + + # hyprpaper: load the Silicon Volt wallpaper on every output + xdg.configFile."hypr/hyprpaper.conf".text = '' + preload = ${config.home.homeDirectory}/.local/share/backgrounds/silicon-volt.png + wallpaper = ,${config.home.homeDirectory}/.local/share/backgrounds/silicon-volt.png + splash = false + ''; + + # Notifications: flat brutalist-glass cards with volt accents + services.mako = { + enable = true; + + settings = { + background-color = "#1c1b1bee"; + text-color = "#e5e2e1"; + border-color = "#cffc00"; + border-size = 1; + border-radius = 0; + width = 420; + margin = "12"; + padding = "14"; + default-timeout = 6000; + font = "JetBrainsMono Nerd Font 11"; + format = "%s\\n%b"; + "icon-location" = "left"; + max-icon-size = 48; + + "urgency=critical" = { + border-color = "#ff7a59"; + text-color = "#ffffff"; + default-timeout = 0; + }; + "urgency=low" = { + border-color = "#2a2929"; + text-color = "#adaaaa"; + }; + }; + }; } diff --git a/home/hypr/hypridle.nix b/home/hypr/hypridle.nix index 714db41..6ebc6ae 100644 --- a/home/hypr/hypridle.nix +++ b/home/hypr/hypridle.nix @@ -8,7 +8,7 @@ general = { lock_cmd = "pidof hyprlock || hyprlock"; before_sleep_cmd = "loginctl lock-session"; - after_sleep_cmd = "hyprctl dispatch dpms on"; + after_sleep_cmd = "hyprctl dispatch \"hl.dsp.dpms({off = false})\""; }; listener = [ @@ -24,10 +24,12 @@ on-timeout = "loginctl lock-session"; } # 3. Turn off displays (DPMS) after 10 minutes + # NOTE: Hyprland's Lua config bridge requires typed dispatcher calls; + # `hyprctl dispatch dpms off` is mangled into invalid Lua. { timeout = 600; - on-timeout = "hyprctl dispatch dpms off"; - on-resume = "hyprctl dispatch dpms on"; + on-timeout = "hyprctl dispatch \"hl.dsp.dpms({off = true})\""; + on-resume = "hyprctl dispatch \"hl.dsp.dpms({off = false})\""; } # 4. Suspend system after 30 minutes { diff --git a/home/hypr/hyprland.nix b/home/hypr/hyprland.nix index 6cb5102..689cd7d 100644 --- a/home/hypr/hyprland.nix +++ b/home/hypr/hyprland.nix @@ -1,10 +1,58 @@ -{ pkgs, lib, ... }: +{ pkgs, lib, config, ... }: let mkLua = lib.generators.mkLuaInline; in { - wayland.windowManager.hyprland = { + # ------------------------------------------------------------------------- + # Per-host monitor configuration. + # + # Hosts declare their displays once, in hosts//default.nix, via: + # home-manager.users.anish.volt.monitors = [ { ... } ]; + # and the setting flows into Hyprland here. The default (empty output + + # preferred mode) works on any machine with any display. + # ------------------------------------------------------------------------- + options.volt.monitors = lib.mkOption { + description = "Hyprland monitor rules for this host"; + type = lib.types.listOf (lib.types.submodule { + options = { + output = lib.mkOption { + type = lib.types.str; + default = ""; + description = "Output name or desc: match. Empty string matches every output."; + }; + mode = lib.mkOption { + type = lib.types.str; + default = "preferred"; + description = "Resolution@refresh, e.g. \"3840x2160@164.99Hz\" or \"preferred\"."; + }; + position = lib.mkOption { + type = lib.types.str; + default = "auto"; + }; + scale = lib.mkOption { + type = lib.types.either lib.types.number lib.types.str; + default = "auto"; + }; + vrr = lib.mkOption { + type = lib.types.nullOr (lib.types.either lib.types.number lib.types.str); + default = null; + description = "Optional per-monitor VRR setting (0/1/on-demand)."; + }; + }; + }); + default = [ + { output = ""; mode = "preferred"; position = "auto"; scale = "auto"; } + ]; + }; + + config = { + wayland.windowManager.hyprland.settings.monitor = map (m: + { inherit (m) output mode position scale; } + // (lib.optionalAttrs (m.vrr != null) { inherit (m) vrr; }) + ) config.volt.monitors; + + wayland.windowManager.hyprland = { enable = true; configType = "lua"; package = null; # Hyprland package is managed system-wide via NixOS programs.hyprland @@ -18,45 +66,16 @@ in browser = { _var = "brave"; }; # ----------------------------------------------------------------------- - # 2. Monitors (hl.monitor) + # 2. Monitors (hl.monitor) - declared per-host via volt.monitors + # (see hosts//default.nix); defaults to any display @ preferred. # ----------------------------------------------------------------------- - monitor = [ - { - output = "eDP-2"; - mode = "3840x2160@144"; - position = "0x0"; - scale = 2; - } - { - output = "desc:Dell Inc. DELL U3419W 28DQ5T2"; - mode = "3440x1440@60"; - position = "0x-1440"; - scale = 1; - } - { - output = "desc:Samsung Electric Company Odyssey Ark H1AK500000"; - mode = "3840x2160@144"; - position = "3840x0"; - scale = 1; - } - { - output = ""; - mode = "preferred"; - position = "auto"; - scale = "auto"; - } - ]; # ----------------------------------------------------------------------- # 3. Environment Variables (hl.env) # ----------------------------------------------------------------------- env = [ { _args = [ "XCURSOR_SIZE" "24" ]; } - { _args = [ "LIBVA_DRIVE_NAME" "nvidia" ]; } { _args = [ "XDG_SESSION_TYPE" "wayland" ]; } - { _args = [ "GBM_BACKEND" "nvidia-drm" ]; } - { _args = [ "WLR_NO_HARDWARE_CURSORS" "1" ]; } - { _args = [ "WLR_DRM_DEVICES" "/dev/dri/card1:/dev/dri/card0" ]; } ]; # ----------------------------------------------------------------------- @@ -76,6 +95,9 @@ in cursor = { inactive_timeout = 3; + # Hardware cursors ON (explicit). Note: this replaces the old + # WLR_NO_HARDWARE_CURSORS=1, which forced software rendering. + no_hardware_cursors = false; }; general = { @@ -83,11 +105,12 @@ in gaps_out = 10; border_size = 2; col = { + # Silicon Volt: electric lime gradient on focus, glass gray at rest active_border = { - colors = [ "rgba(33ccffee)" "rgba(00ff99ee)" ]; + colors = [ "rgba(cffc00ee)" "rgba(e3ff66ee)" ]; angle = 45; }; - inactive_border = "rgba(595959aa)"; + inactive_border = "rgba(ffffff14)"; }; layout = "dwindle"; }; @@ -95,19 +118,19 @@ in group = { groupbar = { font_size = 14; - font_family = "SF Pro Display"; + font_family = "JetBrainsMono Nerd Font"; col = { - active = "rgba(33ccff00)"; - inactive = "rgba(59595900)"; + active = "rgba(cffc0022)"; + inactive = "rgba(ffffff08)"; }; }; col = { border_active = { - colors = [ "rgba(33ccffee)" "rgba(00ff99ee)" ]; + colors = [ "rgba(cffc00ee)" "rgba(e3ff66ee)" ]; angle = 45; }; - border_inactive = "rgba(595959aa)"; + border_inactive = "rgba(ffffff14)"; }; }; @@ -117,19 +140,25 @@ in }; decoration = { - rounding = 10; + rounding = 4; + # Subtle glass blur to match the brutalist-glass aesthetic blur = { - enabled = false; - size = 3; - passes = 1; + enabled = true; + size = 4; + passes = 2; + new_optimizations = true; + ignore_opacity = true; + contrast = 1.1; + brightness = 0.9; + vibrancy = 0.1; }; shadow = { - enabled = false; - range = 4; + enabled = true; + range = 12; render_power = 3; - color = "rgba(1a1a1aee)"; + color = "rgba(00000066)"; }; }; @@ -180,6 +209,9 @@ in (mkLua ''function() hl.exec_cmd("quickshell") hl.exec_cmd("hyprpaper") + hl.exec_cmd("mako") + hl.exec_cmd("wl-paste --type text --watch cliphist store") + hl.exec_cmd("wl-paste --type image --watch cliphist store") hl.exec_cmd("${pkgs.hyprpolkitagent}/libexec/hyprpolkitagent") end'') ]; @@ -196,7 +228,7 @@ in { _args = [ (mkLua "mainMod .. \" + SPACE\"") (mkLua "hl.dsp.exec_cmd(\"quickshell ipc call spotlight toggle || rofi -show combi -show-icons\")") ]; } { _args = [ (mkLua "mainMod .. \" + N\"") (mkLua "hl.dsp.exec_cmd(\"quickshell ipc call controlCenter toggle || swaync-client -t -sw\")") ]; } { _args = [ (mkLua "mainMod .. \" + V\"") (mkLua "hl.dsp.window.float({ action = \"toggle\" })") ]; } - { _args = [ (mkLua "mainMod .. \" + R\"") (mkLua "hl.dsp.exec_cmd(\"quickshell ipc call spotlight toggle || rofi -show combi -show-icons\")") ]; } + { _args = [ (mkLua "mainMod .. \" + R\"") (mkLua "hl.dsp.exec_cmd(\"rofi -show combi\")") ]; } { _args = [ (mkLua "mainMod .. \" + G\"") (mkLua "hl.dsp.group.toggle()") ]; } { _args = [ (mkLua "mainMod .. \" + F\"") (mkLua "hl.dsp.window.fullscreen({ fullscreen = 0 })") ]; } @@ -274,30 +306,30 @@ in { _args = [ "XF86MonBrightnessUp" - (mkLua "hl.dsp.exec_cmd(\"brightnessctl -d acpi_video0 set 5%+\")") + (mkLua "hl.dsp.exec_cmd(\"brightnessctl set 5%+\")") { repeating = true; locked = true; } ]; } { _args = [ "XF86MonBrightnessDown" - (mkLua "hl.dsp.exec_cmd(\"brightnessctl -d acpi_video0 set 5%-\")") + (mkLua "hl.dsp.exec_cmd(\"brightnessctl set 5%-\")") { repeating = true; locked = true; } ]; } - # Lid switch + # Lid switch - generically detects the embedded (eDP*) panel on any laptop { _args = [ "switch:off:Lid Switch" - (mkLua "hl.dsp.exec_cmd(\"hyprctl keyword monitor \\\"eDP-2,3840x2160@144,0x0,2\\\"\")") + (mkLua "hl.dsp.exec_cmd(\"m=$(hyprctl monitors | awk '$1==\\\"Monitor\\\"{print $2; exit}'); [ -n \\\"$m\\\" ] && hyprctl keyword monitor \\\"$m, preferred, auto, auto\\\"\")") { locked = true; } ]; } { _args = [ "switch:on:Lid Switch" - (mkLua "hl.dsp.exec_cmd(\"hyprctl keyword monitor \\\"eDP-2, disable\\\"\")") + (mkLua "hl.dsp.exec_cmd(\"m=$(hyprctl monitors | awk '$1==\\\"Monitor\\\"{print $2; exit}'); [ -n \\\"$m\\\" ] && hyprctl keyword monitor \\\"$m, disable\\\"\")") { locked = true; } ]; } @@ -364,5 +396,6 @@ in } ]; }; + }; }; } diff --git a/home/hypr/hyprlock.nix b/home/hypr/hyprlock.nix index 829b03d..adadb38 100644 --- a/home/hypr/hyprlock.nix +++ b/home/hypr/hyprlock.nix @@ -10,41 +10,65 @@ grace = 0; hide_cursor = true; no_fade_in = false; + ignore_empty_input = true; }; - # Background with screenshot blur effect + # Background: screenshot darkened into the site's #0e0e0e glass tone background = [ { path = "screenshot"; - blur_passes = 3; - blur_size = 8; + blur_passes = 4; + blur_size = 12; noise = 0.0117; - contrast = 0.8916; - brightness = 0.8172; - vibrancy = 0.1696; - vibrancy_darkness = 0.0; + contrast = 0.9; + brightness = 0.55; + vibrancy = 0.1; + vibrancy_darkness = 0.4; } ]; - # Clock display label = [ + # Big clock { monitor = ""; text = "$TIME"; - color = "rgba(242, 243, 244, 0.75)"; - font_size = 95; - font_family = "JetBrainsMono Nerd Font Bold"; - position = "0, 200"; + color = "rgba(ffffffdd)"; + font_size = 90; + font_family = "JetBrainsMono Nerd Font"; + position = "0, 220"; halign = "center"; valign = "center"; } + # Date { monitor = ""; - text = "cmd[update:1000] echo \"$(date +'%A, %B %d')\""; - color = "rgba(242, 243, 244, 0.6)"; - font_size = 22; + text = "cmd[update:1000] echo \" $(date +'%A, %B %d') \""; + color = "rgba(adaaaaff)"; + font_size = 20; font_family = "JetBrainsMono Nerd Font"; - position = "0, 100"; + position = "0, 120"; + halign = "center"; + valign = "center"; + } + # Session banner + { + monitor = ""; + text = "NIXOS // SILICON VOLT"; + color = "rgba(cffc00ee)"; + font_size = 14; + font_family = "JetBrainsMono Nerd Font"; + position = "0, 60"; + halign = "center"; + valign = "center"; + } + # User + hostname line + { + monitor = ""; + text = "cmd[update:3600000] echo \" $USER @ $(hostname) \""; + color = "rgba(adaaaa99)"; + font_size = 13; + font_family = "JetBrainsMono Nerd Font"; + position = "0, -160"; halign = "center"; valign = "center"; } @@ -54,18 +78,22 @@ input-field = [ { monitor = ""; - size = "250, 50"; + size = "300, 50"; outline_thickness = 2; dots_size = 0.2; dots_spacing = 0.2; dots_center = true; - outer_color = "rgba(33ccffee)"; - inner_color = "rgba(15, 23, 42, 0.85)"; - font_color = "rgb(242, 243, 244)"; + # Volt lime outline, dark glass interior + outer_color = "rgba(cffc00ee)"; + inner_color = "rgba(1c1b1b, 0.9)"; + check_color = "rgba(e3ff66ee)"; + fail_color = "rgba(ff7a59ee)"; + font_color = "rgba(e5e2e1ff)"; + fail_text = "$FAIL (attempts: $ATTEMPTS)"; fade_on_empty = false; - placeholder_text = "Enter Password..."; + placeholder_text = " Enter Password..."; hide_input = false; - position = "0, -50"; + position = "0, -80"; halign = "center"; valign = "center"; } diff --git a/home/hypr/quickshell/GlobalState.qml b/home/hypr/quickshell/GlobalState.qml index a436898..45056f1 100644 --- a/home/hypr/quickshell/GlobalState.qml +++ b/home/hypr/quickshell/GlobalState.qml @@ -39,6 +39,7 @@ QtObject { property int brightness: 100 property bool wifiConnected: true + property string connectionType: "off" // "wifi" | "eth" | "off" property string wifiSsid: "Wi-Fi" property string ipAddress: "" @@ -47,6 +48,11 @@ QtObject { property bool batteryCharging: false property string batteryStatus: "Full" + // Disk Monitoring (root filesystem) + property int diskPercent: 0 + property string diskUsedGb: "0" + property string diskTotalGb: "0" + // GPU VRAM Monitoring (NVIDIA) property string gpuVramUsed: "0" property string gpuVramTotal: "0" @@ -98,31 +104,32 @@ QtObject { } function switchWorkspace(n) { - Hyprland.dispatch("workspace " + n); + // NOTE: Hyprland's Lua config bridge requires typed dispatcher calls + // (plain `hyprctl dispatch workspace 2` is mangled into invalid Lua). + Hyprland.dispatch("hl.dsp.focus({workspace = " + n + "})"); } // Hyprland raw event processing readonly property Connections hyprConn: Connections { target: Hyprland - function onRawEvent(event) { console.log(JSON.stringify(event), event.name, event.event, event.data); - if (!event) return; - if (event.startsWith("workspace>>")) { - const data = event.substring(11); + function onRawEvent(event) { + if (!event || !event.name) return; + const name = event.name; + const data = event.data !== undefined ? String(event.data) : ""; + + if (name === "workspace") { const wsNum = parseInt(data); if (!isNaN(wsNum)) root.activeWorkspace = wsNum; - } else if (event.startsWith("activewindow>>")) { - const data = event.substring(14); + } else if (name === "activewindow") { const parts = data.split(","); - const title = parts[1] ? parts[1].trim() : (parts[0] ? parts[0].trim() : ""); - const cls = parts[0] ? parts[0].trim() : "Finder"; - - root.activeWindowClass = cls && cls.length > 0 ? cls : "Finder"; + const cls = parts[0] ? parts[0].trim() : ""; + const title = parts[1] ? parts[1].trim() : (cls || ""); + + root.activeWindowClass = cls && cls.length > 0 ? cls : "Desktop"; root.activeWindowTitle = title && title.length > 0 ? title : root.activeWindowClass; - } else if (event.startsWith("activewindowv2>>")) { + } else if (name === "activewindowv2" || name === "openwindow" || name === "closewindow") { // Update running clients clientsProc.running = true; - } else if (event.startsWith("openwindow>>") || event.startsWith("closewindow>>")) { - clientsProc.running = true; } } } @@ -156,6 +163,7 @@ QtObject { brightProc.running = true; mprisProc.running = true; gpuProc.running = true; + diskProc.running = true; } } @@ -200,15 +208,16 @@ QtObject { } readonly property Process wifiProc: Process { - command: ["bash", "-c", "ssid=\"\"; if command -v nmcli &>/dev/null; then ssid=$(nmcli -t -f active,ssid dev wifi 2>/dev/null | grep '^yes:' | cut -d':' -f2); fi; [ -z \"$ssid\" ] && [ -f /proc/net/wireless ] && ssid=$(iwgetid -r 2>/dev/null); ip=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}'); echo \"${ssid:-Ethernet} ${ip:-Offline}\""] + // Emits: connectionType|ssid|ip ("wifi" | "eth" | "off") + command: ["bash", "-c", "route=$(ip -4 route get 1.1.1.1 2>/dev/null); ip=$(echo \"$route\" | awk '{for(i=1;i<=NF;i++) if($i==\"src\") print $(i+1)}'); dev=$(echo \"$route\" | awk '{for(i=1;i<=NF;i++) if($i==\"dev\") print $(i+1)}'); type=\"off\"; ssid=\"\"; if [ -n \"$dev\" ]; then if [ -d \"/sys/class/net/$dev/wireless\" ]; then type=\"wifi\"; ssid=$(iwgetid -r 2>/dev/null); if [ -z \"$ssid\" ] && command -v nmcli &>/dev/null; then ssid=$(nmcli -t -f active,ssid dev wifi 2>/dev/null | grep '^yes:' | cut -d':' -f2); fi; else type=\"eth\"; fi; fi; echo \"$type|$ssid|$ip\""] stdout: StdioCollector { onDataChanged: { - const str = String(data).trim(); - const parts = str.split(" "); - if (parts.length >= 2) { - root.wifiSsid = parts[0]; - root.ipAddress = parts[1]; - root.wifiConnected = parts[1] !== "Offline"; + const parts = String(data).trim().split("|"); + if (parts.length >= 3) { + root.connectionType = parts[0] !== "" ? parts[0] : "off"; + root.wifiSsid = parts[1]; + root.ipAddress = parts[2]; + root.wifiConnected = parts[2] !== "" && parts[2] !== "Offline"; } } } @@ -237,6 +246,21 @@ QtObject { } } + readonly property Process diskProc: Process { + // Emits: usedGB totalGB percent for the root filesystem + command: ["bash", "-c", "df -BG / 2>/dev/null | awk 'NR==2 {u=$3; t=$2; gsub(/G/,\"\",u); gsub(/G/,\"\",t); if (t+0 > 0) print u, t, int(u*100/t)}'"] + stdout: StdioCollector { + onDataChanged: { + const parts = String(data).trim().split(" "); + if (parts.length >= 3) { + root.diskUsedGb = parts[0]; + root.diskTotalGb = parts[1]; + root.diskPercent = parseInt(parts[2]) || 0; + } + } + } + } + readonly property Process mprisProc: Process { command: ["bash", "-c", "if command -v playerctl &>/dev/null; then t=$(playerctl metadata title 2>/dev/null); a=$(playerctl metadata artist 2>/dev/null); s=$(playerctl status 2>/dev/null); echo \"$s|$t|$a\"; fi"] stdout: StdioCollector { @@ -287,40 +311,40 @@ QtObject { } } - // Helper functions to control system + // Helper functions to control system (execDetached: fire-and-forget) function setVolume(val) { root.volume = Math.max(0, Math.min(100, val)); - Process.exec(["pactl", "set-sink-volume", "@DEFAULT_SINK@", `${root.volume}%`]); + Quickshell.execDetached(["pactl", "set-sink-volume", "@DEFAULT_SINK@", `${root.volume}%`]); } function toggleVolumeMute() { - Process.exec(["pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle"]); + Quickshell.execDetached(["pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle"]); root.volumeMuted = !root.volumeMuted; } function setBrightness(val) { root.brightness = Math.max(1, Math.min(100, val)); - Process.exec(["brightnessctl", "set", `${root.brightness}%`]); + Quickshell.execDetached(["brightnessctl", "set", `${root.brightness}%`]); } function mediaPlayPause() { - Process.exec(["playerctl", "play-pause"]); + Quickshell.execDetached(["playerctl", "play-pause"]); mprisProc.running = true; } function mediaNext() { - Process.exec(["playerctl", "next"]); + Quickshell.execDetached(["playerctl", "next"]); mprisProc.running = true; } function mediaPrevious() { - Process.exec(["playerctl", "previous"]); + Quickshell.execDetached(["playerctl", "previous"]); mprisProc.running = true; } function launchApp(execCmd) { if (!execCmd) return; - Process.exec(["bash", "-c", `${execCmd} &`]); + Quickshell.execDetached(["sh", "-c", execCmd]); closeAllPopups(); spotlightVisible = false; } diff --git a/home/hypr/quickshell/components/AppMenuDropdown.qml b/home/hypr/quickshell/components/AppMenuDropdown.qml index ee24792..ad58b53 100644 --- a/home/hypr/quickshell/components/AppMenuDropdown.qml +++ b/home/hypr/quickshell/components/AppMenuDropdown.qml @@ -25,7 +25,7 @@ Rectangle { model: root.menuName === "File" ? [ { text: "New Window", shortcut: "⌘N", cmd: "foot &" }, { text: "New Incognito Window", shortcut: "⇧⌘N", cmd: "brave --incognito &" }, - { text: "Close Window", shortcut: "⌘W", cmd: "hyprctl dispatch killactive" }, + { text: "Close Window", shortcut: "⌘W", cmd: "hyprctl dispatch \"hl.dsp.window.close()\"" }, { text: "---", shortcut: "", cmd: "" }, { text: "Take Screenshot", shortcut: "⇧⌘5", cmd: "grim -g \"$(slurp)\" ~/screenshot.png" } ] : [] @@ -49,9 +49,9 @@ Rectangle { // VIEW MENU Repeater { model: root.menuName === "View" ? [ - { text: "Toggle Fullscreen", shortcut: "⌃⌘F", cmd: "hyprctl dispatch fullscreen 0" }, - { text: "Toggle Floating", shortcut: "⌘V", cmd: "hyprctl dispatch togglefloating" }, - { text: "Toggle Group", shortcut: "⌘G", cmd: "hyprctl dispatch togglegroup" }, + { text: "Toggle Fullscreen", shortcut: "⌃⌘F", cmd: "hyprctl dispatch \"hl.dsp.window.fullscreen({fullscreen = 0})\"" }, + { text: "Toggle Floating", shortcut: "⌘V", cmd: "hyprctl dispatch \"hl.dsp.window.float({action = \"toggle\"})\"" }, + { text: "Toggle Group", shortcut: "⌘G", cmd: "hyprctl dispatch \"hl.dsp.group.toggle()\"" }, { text: "---", shortcut: "", cmd: "" }, { text: "Zoom In", shortcut: "⌘+", cmd: "" }, { text: "Zoom Out", shortcut: "⌘-", cmd: "" } @@ -62,12 +62,12 @@ Rectangle { // WINDOW MENU Repeater { model: root.menuName === "Window" ? [ - { text: "Minimize Window", shortcut: "⌘M", cmd: "hyprctl dispatch movetoworkspacesilent special:scratch" }, - { text: "Tile Left", shortcut: "⌃⌥←", cmd: "hyprctl dispatch movewindow l" }, - { text: "Tile Right", shortcut: "⌃⌥→", cmd: "hyprctl dispatch movewindow r" }, + { text: "Minimize Window", shortcut: "⌘M", cmd: "hyprctl dispatch \"hl.dsp.window.move({workspace = \"special:scratch\"})\"" }, + { text: "Tile Left", shortcut: "⌃⌥←", cmd: "hyprctl dispatch \"hl.dsp.window.move({direction = \"left\"})\"" }, + { text: "Tile Right", shortcut: "⌃⌥→", cmd: "hyprctl dispatch \"hl.dsp.window.move({direction = \"right\"})\"" }, { text: "---", shortcut: "", cmd: "" }, - { text: "Show Scratchpad", shortcut: "⌘↩", cmd: "hyprctl dispatch togglespecialworkspace scratch" }, - { text: "Show Messaging", shortcut: "⌘M", cmd: "hyprctl dispatch togglespecialworkspace messaging" } + { text: "Show Scratchpad", shortcut: "⌘↩", cmd: "hyprctl dispatch \"hl.dsp.workspace.toggle_special(\"scratch\")\"" }, + { text: "Show Messaging", shortcut: "⌘M", cmd: "hyprctl dispatch \"hl.dsp.workspace.toggle_special(\"messaging\")\"" } ] : [] delegate: MenuEntryDelegate {} } diff --git a/home/hypr/quickshell/components/AppleMenu.qml b/home/hypr/quickshell/components/AppleMenu.qml index 5b62652..8382b27 100644 --- a/home/hypr/quickshell/components/AppleMenu.qml +++ b/home/hypr/quickshell/components/AppleMenu.qml @@ -99,7 +99,7 @@ Rectangle { shortcut: "⇧⌘Q" onTriggered: { GlobalState.appleMenuVisible = false; - GlobalState.launchApp("hyprctl dispatch exit"); + GlobalState.launchApp("loginctl terminate-session"); } } } diff --git a/home/hypr/quickshell/components/Dock.qml b/home/hypr/quickshell/components/Dock.qml index 50d3343..2d29eb5 100644 --- a/home/hypr/quickshell/components/Dock.qml +++ b/home/hypr/quickshell/components/Dock.qml @@ -170,7 +170,7 @@ PanelWindow { isPinned: false onCustomClicked: { if (modelData.address) { - Hyprland.dispatch(`focuswindow address:${modelData.address}`); + Hyprland.dispatch(`hl.dsp.focus({window = "address:${modelData.address}"})`); } } } diff --git a/home/hypr/quickshell/components/SidePanel.qml b/home/hypr/quickshell/components/SidePanel.qml index c00d69e..145b0d0 100644 --- a/home/hypr/quickshell/components/SidePanel.qml +++ b/home/hypr/quickshell/components/SidePanel.qml @@ -308,8 +308,9 @@ PanelWindow { var seen = {} if (GlobalState.runningApps) { for (var i = 0; i < GlobalState.runningApps.length; i++) { - var cls = GlobalState.runningApps[i] - if (cls && pinned.indexOf(cls) === -1 && !seen[cls]) { + // hyprctl clients entries are objects; extract the class string + var cls = GlobalState.runningApps[i] && GlobalState.runningApps[i].class + if (cls && cls.length > 0 && pinned.indexOf(cls) === -1 && !seen[cls]) { seen[cls] = true running.push(cls) } @@ -331,7 +332,7 @@ PanelWindow { Text { anchors.centerIn: parent - text: modelData.charAt(0).toUpperCase() + text: modelData && modelData.length > 0 ? modelData.charAt(0).toUpperCase() : "?" color: Theme.textPrimary font.family: Theme.fontSans font.pixelSize: 12 diff --git a/home/hypr/quickshell/components/Spotlight.qml b/home/hypr/quickshell/components/Spotlight.qml index b818c35..bf8d923 100644 --- a/home/hypr/quickshell/components/Spotlight.qml +++ b/home/hypr/quickshell/components/Spotlight.qml @@ -31,7 +31,9 @@ PanelWindow { // App Database Loader Process readonly property Process appScanProc: Process { - command: ["bash", "-c", "dirs=(\"/run/current-system/sw/share/applications\" \"$HOME/.nix-profile/share/applications\" \"$HOME/.local/share/applications\"); echo '['; first=1; for d in \"${dirs[@]}\"; do [ -d \"$d\" ] && for f in \"$d\"/*.desktop; do [ -e \"$f\" ] || continue; grep -q \"^NoDisplay=true\" \"$f\" 2>/dev/null && continue; n=$(grep -m 1 \"^Name=\" \"$f\" 2>/dev/null | cut -d= -f2-); e=$(grep -m 1 \"^Exec=\" \"$f\" 2>/dev/null | cut -d= -f2- | sed -E 's/%[fFuUdDnNickvm]//g'); if [ -n \"$n\" ] && [ -n \"$e\" ]; then [ $first -eq 0 ] && echo ','; first=0; echo \"{\\\"name\\\":\\\"$n\\\",\\\"exec\\\":\\\"$e\\\"}\"; fi; done; done; echo ']'"] + // Indexes .desktop applications AND executable user scripts so the + // launcher finds "everything" without depending on an external service. + command: ["bash", "-c", "dirs=(\"/run/current-system/sw/share/applications\" \"$HOME/.nix-profile/share/applications\" \"$HOME/.local/share/applications\"); bdirs=(\"$HOME/.local/bin\" \"$HOME/bin\" \"$HOME/.cargo/bin\"); echo '['; first=1; for d in \"${dirs[@]}\"; do [ -d \"$d\" ] && for f in \"$d\"/*.desktop; do [ -e \"$f\" ] || continue; grep -q \"^NoDisplay=true\" \"$f\" 2>/dev/null && continue; n=$(grep -m 1 \"^Name=\" \"$f\" 2>/dev/null | cut -d= -f2-); e=$(grep -m 1 \"^Exec=\" \"$f\" 2>/dev/null | cut -d= -f2- | sed -E 's/%[fFuUdDnNickvm]//g'); if [ -n \"$n\" ] && [ -n \"$e\" ]; then [ $first -eq 0 ] && echo ','; first=0; echo \"{\\\"name\\\":\\\"$n\\\",\\\"exec\\\":\\\"$e\\\"}\"; fi; done; done; for d in \"${bdirs[@]}\"; do [ -d \"$d\" ] || continue; for f in \"$d\"/*; do [ -f \"$f\" ] && [ -x \"$f\" ] || continue; n=$(basename \"$f\"); [ $first -eq 0 ] && echo ','; first=0; printf '{\\\"name\\\":\\\"%s\\\",\\\"exec\\\":\\\"%s\\\",\\\"comment\\\":\\\"script\\\"}\\n' \"$n\" \"$f\"; done; done; echo ']'"] stdout: StdioCollector { onDataChanged: { try { diff --git a/home/hypr/quickshell/components/SystemHUD.qml b/home/hypr/quickshell/components/SystemHUD.qml index c42bc00..e3116c9 100644 --- a/home/hypr/quickshell/components/SystemHUD.qml +++ b/home/hypr/quickshell/components/SystemHUD.qml @@ -73,6 +73,7 @@ PanelWindow { component ToggleCard: Rectangle { property string title: "" property string icon: "" + property string glyph: "" // Nerd Font glyph alternative to icon file property bool active: false signal clicked() @@ -97,15 +98,22 @@ PanelWindow { anchors.fill: parent anchors.margins: 12 spacing: 12 + Text { + visible: parent.parent.glyph !== "" + text: parent.parent.glyph + font.family: "JetBrainsMono Nerd Font" + font.pixelSize: 20 + color: parent.parent.active ? Theme.accentVolt : Theme.textMuted + } Image { - source: icon ? Qt.resolvedUrl("../" + icon) : "" + source: parent.parent.icon && parent.parent.glyph === "" ? Qt.resolvedUrl("../" + parent.parent.icon) : "" Layout.preferredWidth: 24 Layout.preferredHeight: 24 sourceSize: Qt.size(24, 24) - visible: parent.icon !== "" + visible: parent.parent.icon !== "" && parent.parent.glyph === "" } Text { - text: title + text: parent.parent.title color: Theme.textPrimary font.family: Theme.fontSans font.pixelSize: 13 @@ -312,8 +320,7 @@ PanelWindow { icon: "assets/wifi.svg" active: GlobalState.wifiConnected onClicked: { - let proc = Quickshell.createProcess("sh", ["-c", "nm-connection-editor || foot -e nmtui"]) - proc.start() + Quickshell.execDetached(["sh", "-c", "nm-connection-editor || foot -e nmtui"]) } } @@ -330,14 +337,14 @@ PanelWindow { ToggleCard { property bool dndActive: false title: "Do Not Disturb" - icon: "" // Optional DND icon + glyph: "\uF186" active: dndActive onClicked: dndActive = !dndActive } ToggleCard { title: GlobalState.volumeMuted ? "Muted" : "Sound" - icon: "" // Optional sound icon + glyph: GlobalState.volumeMuted ? "\uF026" : "\uF028" active: !GlobalState.volumeMuted onClicked: GlobalState.toggleVolumeMute() } @@ -423,6 +430,13 @@ PanelWindow { valueText: GlobalState.memUsedGb + " / " + GlobalState.memTotalGb + " GB" } + ProgressBarItem { + label: "DISK" + percent: GlobalState.diskPercent + valueText: GlobalState.diskUsedGb + " / " + GlobalState.diskTotalGb + " GB" + barColor: GlobalState.diskPercent > 90 ? Theme.accentCoral : Theme.accentVolt + } + ProgressBarItem { label: "VRAM" percent: (GlobalState.gpuVramUsed / Math.max(1, GlobalState.gpuVramTotal)) * 100 @@ -446,7 +460,12 @@ PanelWindow { color: GlobalState.wifiConnected ? Theme.accentVolt : Theme.accentCoral } Text { - text: GlobalState.wifiConnected ? "Connected" : "Disconnected" + text: { + if (!GlobalState.wifiConnected) return "Disconnected"; + if (GlobalState.connectionType === "wifi") return "Wi-Fi — " + (GlobalState.wifiSsid !== "" ? GlobalState.wifiSsid : "unknown"); + if (GlobalState.connectionType === "eth") return "Ethernet"; + return "Connected"; + } color: Theme.textPrimary font.family: Theme.fontSans font.pixelSize: 14 @@ -454,14 +473,8 @@ PanelWindow { } Text { - text: "SSID: " + (GlobalState.wifiSsid !== "" ? GlobalState.wifiSsid : "None") - color: Theme.textSecondary - font.family: Theme.fontSans - font.pixelSize: 13 - } - - Text { - text: "IP: " + GlobalState.ipAddress + visible: GlobalState.wifiConnected + text: "IP: " + (GlobalState.ipAddress !== "" ? GlobalState.ipAddress : "—") color: Theme.textSecondary font.family: Theme.fontMono font.pixelSize: 13 @@ -478,8 +491,7 @@ PanelWindow { onEntered: parent.color = Theme.textPrimary onExited: parent.color = Theme.textMuted onClicked: { - let proc = Quickshell.createProcess("sh", ["-c", "nm-connection-editor || foot -e nmtui"]) - proc.start() + Quickshell.execDetached(["sh", "-c", "nm-connection-editor || foot -e nmtui"]) } } } @@ -558,7 +570,7 @@ PanelWindow { } } Text { - text: GlobalState.mediaStatus === "Playing" ? "⏸" : "▶" + text: GlobalState.mediaPlaying ? "⏸" : "▶" font.pixelSize: 24 color: Theme.textSecondary MouseArea { diff --git a/home/hypr/quickshell/components/TopStrip.qml b/home/hypr/quickshell/components/TopStrip.qml index 1e9924b..030c43a 100644 --- a/home/hypr/quickshell/components/TopStrip.qml +++ b/home/hypr/quickshell/components/TopStrip.qml @@ -5,6 +5,11 @@ import Quickshell.Wayland import "../theme" import ".." +// Nerd Font glyphs (JetBrainsMono Nerd Font, installed system-wide) +// \uF1EB wifi \uF796 ethernet \uF538 memory \uF1C0 disk +// \uF028 volume \uF185 brightness \uF240 battery \uF0E7 charging +// \uF017 clock \uF2DB cpu + PanelWindow { id: root @@ -38,33 +43,87 @@ PanelWindow { color: Theme.glassBorder } + // -------------------------------------------------------------------- + // Helpers to compose rich-text status segments + // -------------------------------------------------------------------- + function pad(num) { + if (num === undefined || num === null) return "00"; + return num < 10 ? "0" + num : num.toString(); + } + + function seg(icon, value, iconColor, valueColor) { + return "" + icon + " " + value + ""; + } + + function buildStats() { + const c = "#e5e2e1"; + const dim = "#adaaaa"; + const volt = "#CFFC00"; + const coral = "#FF7A59"; + const sep = " // "; + const parts = []; + + // Network: type icon + SSID (wifi) + IP + if (GlobalState.wifiConnected) { + const icon = GlobalState.connectionType === "wifi" ? "\uF1EB" : "\uF796"; + const label = (GlobalState.connectionType === "wifi" && GlobalState.wifiSsid !== "") ? GlobalState.wifiSsid : "eth"; + parts.push(seg(icon, label + " " + (GlobalState.ipAddress !== "" ? GlobalState.ipAddress : "\u2014"), volt, c)); + } else { + parts.push(seg("\uF1EB", "offline", coral, dim)); + } + + // Memory (used GB / total GB) + parts.push(seg("\uF538", GlobalState.memUsedGb + "G / " + GlobalState.memTotalGb + "G", + GlobalState.memPercent > 85 ? coral : dim, c)); + + // Disk (root filesystem percent) + parts.push(seg("\uF1C0", GlobalState.diskPercent + "%", + GlobalState.diskPercent > 90 ? coral : dim, c)); + + // Audio (mute state flips icon + color) + if (GlobalState.volumeMuted) { + parts.push(seg("\uF026", "mute", coral, dim)); + } else { + parts.push(seg("\uF028", GlobalState.volume + "%", dim, c)); + } + + // Display brightness + parts.push(seg("\uF185", GlobalState.brightness + "%", dim, c)); + + // Battery (only when present, e.g. laptops) + if (GlobalState.batteryPresent) { + const icon = GlobalState.batteryCharging ? "\uF0E7" : "\uF240"; + parts.push(seg(icon, GlobalState.batteryPercent + "%", + GlobalState.batteryPercent < 20 ? coral : dim, c)); + } + + return parts.join(sep); + } + RowLayout { anchors.fill: parent anchors.leftMargin: 16 anchors.rightMargin: 16 spacing: 16 - // Left Section + // Left Section: workspace indicator Text { id: workspaceIndicator Layout.alignment: Qt.AlignVCenter - font.family: Theme.fontMono + textFormat: Text.RichText + font.family: "JetBrainsMono Nerd Font" font.pixelSize: 12 - color: Theme.textMuted - - function pad(num) { - if (num === undefined || num === null) return "00"; - return num < 10 ? "0" + num : num.toString(); - } - text: pad(GlobalState.activeWorkspace) + " // " + (GlobalState.workspaceNames[GlobalState.activeWorkspace] !== undefined ? GlobalState.workspaceNames[GlobalState.activeWorkspace] : "main") - - Behavior on color { - ColorAnimation { duration: 150 } + text: { + const name = GlobalState.workspaceNames[GlobalState.activeWorkspace] !== undefined + ? GlobalState.workspaceNames[GlobalState.activeWorkspace] : "main"; + return "" + pad(GlobalState.activeWorkspace) + "" + + " // " + + "" + name + ""; } } - // Center Section + // Center Section: focused window title Text { id: windowTitle Layout.fillWidth: true @@ -84,18 +143,35 @@ PanelWindow { } } - // Right Section + // Right Section: live system stats (click to open the System HUD) + Text { + id: stats + Layout.alignment: Qt.AlignVCenter + textFormat: Text.RichText + font.family: "JetBrainsMono Nerd Font" + font.pixelSize: 11 + + text: buildStats() + + MouseArea { + id: statsMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: GlobalState.toggleSystemHud() + } + } + + // Clock Text { id: clock Layout.alignment: Qt.AlignVCenter - font.family: Theme.fontMono + textFormat: Text.RichText + font.family: "JetBrainsMono Nerd Font" font.pixelSize: 12 - color: Theme.textPrimary - text: GlobalState.timeString - Behavior on color { - ColorAnimation { duration: 150 } - } + text: "\uF017" + + " " + GlobalState.timeString + "" } } } diff --git a/home/hypr/wallpaper.svg b/home/hypr/wallpaper.svg new file mode 100644 index 0000000..eaa3cea --- /dev/null +++ b/home/hypr/wallpaper.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/home/rofi.nix b/home/rofi.nix new file mode 100644 index 0000000..c12cf9e --- /dev/null +++ b/home/rofi.nix @@ -0,0 +1,28 @@ +{ pkgs, ... }: + +let + # Rofi script-mode indexer: exposes every executable user script + # (shell, python, whatever) so the launcher finds "everything". + rofiScripts = pkgs.writeShellScriptBin "rofi-scripts" + (builtins.readFile ./rofi/rofi-scripts.sh); +in +{ + programs.rofi = { + enable = true; + terminal = "${pkgs.foot}/bin/foot"; + cycle = true; + theme = ./rofi/volt.rasi; + + extraConfig = { + # Combi mode searches .desktop apps (drun), binaries (run), + # open windows (window) and user scripts (scripts). + modes = "combi"; + combi-modes = "drun,run,window,scripts:${rofiScripts}/bin/rofi-scripts"; + show-icons = false; + drun-display-format = "{name} {comment}"; + display-combi = "silicon volt >"; + matching = "fuzzy"; + sort = true; + }; + }; +} diff --git a/home/rofi/rofi-scripts.sh b/home/rofi/rofi-scripts.sh new file mode 100644 index 0000000..9b8d1bf --- /dev/null +++ b/home/rofi/rofi-scripts.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Rofi script-mode: indexes every executable user script so the launcher +# can find shell scripts and everything else, not just .desktop apps. + +declare -A SCRIPTS + +for dir in "$HOME/.local/bin" "$HOME/bin" "$HOME/.cargo/bin" "$HOME/Tools/bin" "$HOME/scripts"; do + [ -d "$dir" ] || continue + while IFS= read -r -d '' f; do + [ -f "$f" ] || continue + b=$(basename "$f") + SCRIPTS["$b"]="$f" + done < <(find "$dir" -maxdepth 1 -type f -executable -print0 2>/dev/null) +done + +sel="${1:-}" +if [ -n "$sel" ] && [ -n "${SCRIPTS[$sel]:-}" ]; then + exec setsid "${SCRIPTS[$sel]}" >/dev/null 2>&1 +fi + +printf '%s\n' "${!SCRIPTS[@]}" | sort diff --git a/home/rofi/volt.rasi b/home/rofi/volt.rasi new file mode 100644 index 0000000..a29acfa --- /dev/null +++ b/home/rofi/volt.rasi @@ -0,0 +1,135 @@ +// Silicon Volt - Rofi theme (anish.govind.cc palette) +// Flat brutalist-glass: dark glass surfaces, volt-lime selection, 0 radius. + +* { + bg0: rgba( 14, 14, 14, 95 % ); + bg1: rgba( 28, 27, 27, 95 % ); + fg0: #e5e2e1; + fg1: #ffffff; + fg2: #adaaaa; + volt: #CFFC00; + volt-dim: rgba( 207, 252, 0, 10 % ); + coral: #FF7A59; + border0: rgba( 255, 255, 255, 8 % ); + + background-color: transparent; + text-color: @fg0; + font: "JetBrainsMono Nerd Font 12"; + + margin: 0; + padding: 0; + spacing: 0; +} + +window { + location: north; + width: 680; + y-offset: 140; + background-color: @bg0; + border: 1; + border-color: @border0; +} + +mainbox { + children: [ inputbar, message, listview ]; +} + +inputbar { + background-color: @bg1; + border: 0 0 1 0; + border-color: @border0; + padding: 14 18; + spacing: 12; + children: [ prompt, entry ]; +} + +prompt { + text-color: @volt; +} + +entry { + text-color: @fg1; + placeholder: "search apps, scripts, windows..."; + placeholder-color: @fg2; + cursor: text; + cursor-color: @volt; +} + +message { + background-color: @volt-dim; + padding: 10 18; + text-color: @fg2; +} + +listview { + lines: 12; + fixed-height: false; + padding: 6 0; + scrollbar: true; +} + +scrollbar { + handle-width: 2; + handle-color: @volt; + padding: 6 2 6 0; +} + +element { + padding: 10 18; + text-color: @fg0; + border: 0 0 0 2; + border-color: transparent; +} + +element normal normal { + background-color: transparent; + text-color: @fg0; +} + +element normal active { + text-color: @volt; +} + +element selected normal { + background-color: @volt-dim; + border-color: @volt; + text-color: @fg1; +} + +element selected active { + background-color: @volt-dim; + border-color: @volt; + text-color: @volt; +} + +element alternate normal { + background-color: transparent; +} + +element-text { + text-color: inherit; + vertical-align: 0.5; +} + +element-icon { + size: 0; +} + +mode-switcher { + padding: 8 12; + border: 0 0 1 0; + border-color: @border0; + background-color: @bg1; +} + +button { + text-color: @fg2; + padding: 6 14; + border: 0 0 2 0; + border-color: transparent; +} + +button selected { + text-color: @volt; + border-color: @volt; +} diff --git a/home/scripts/herdr-picker.sh b/home/scripts/herdr-picker.sh index 5b2c736..5e3ad40 100755 --- a/home/scripts/herdr-picker.sh +++ b/home/scripts/herdr-picker.sh @@ -41,13 +41,16 @@ done menu_items+=("🐚 [Skip - Plain Shell]") -# Interactive session selector using fzf +# Interactive session selector using fzf (Silicon Volt palette) if command -v fzf &>/dev/null; then selected=$(printf '%s\n' "${menu_items[@]}" | fzf \ --height=45% \ --layout=reverse \ --border=rounded \ - --prompt="󱓞 Herdr Session > " \ + --prompt=" Herdr Session > " \ + --color=fg:#e5e2e1,bg:#131313,hl:#CFFC00,fg+:#ffffff,bg+:#1c1b1b,hl+:#e3ff66 \ + --color=info:#ADAAAA,prompt:#CFFC00,pointer:#FF7A59,marker:#CFFC00,spinner:#00BFFF \ + --color=border:#2a2929,gutter:#131313 \ --header="Select Herdr session to attach / create | Esc for plain shell" \ --header-first) else diff --git a/home/starship/starship.toml b/home/starship/starship.toml index 4af853c..2c52cc2 100644 --- a/home/starship/starship.toml +++ b/home/starship/starship.toml @@ -1,4 +1,4 @@ -# macOS Catalina Dark Aesthetic Starship Theme (Single-Line) +# Silicon Volt Starship Theme (anish.govind.cc palette, single-line) add_newline = false scan_timeout = 10 @@ -6,49 +6,49 @@ format = "$os$directory$git_branch$git_status$nix_shell$rust$c$python$cmd_durati [os] disabled = false -style = "bold #f2f2f7" -symbols = { NixOS = " " } +style = "bold #e5e2e1" +symbols = { NixOS = " " } [directory] truncation_length = 3 truncate_to_repo = true -style = "bold #0a84ff" +style = "bold #CFFC00" format = "[$path]($style) " [git_branch] -symbol = " " -style = "bold #bf5af2" +symbol = " " +style = "bold #00BFFF" format = "on [$symbol$branch]($style) " [git_status] -style = "bold #ff453a" +style = "bold #FF7A59" format = "([$all_status$ahead_behind]($style) )" [nix_shell] -symbol = " " -style = "bold #64d2ff" +symbol = " " +style = "bold #e3ff66" format = "via [$symbol$state]($style) " [rust] -symbol = " " -style = "bold #ff9f0a" +symbol = " " +style = "bold #e3ff66" format = "[$symbol($version )]($style)" [c] -symbol = " " -style = "bold #5e5ce6" +symbol = " " +style = "bold #00BFFF" format = "[$symbol($version )]($style)" [python] -symbol = " " -style = "bold #ffd60a" +symbol = " " +style = "bold #CFFC00" format = "[$symbol($version )]($style)" [cmd_duration] min_time = 2000 -style = "bold #ffd60a" +style = "bold #FF7A59" format = "took [$duration]($style) " [character] -success_symbol = "[➜](bold #30d158) " -error_symbol = "[➜](bold #ff453a) " +success_symbol = "[➜](bold #CFFC00) " +error_symbol = "[➜](bold #FF7A59) " diff --git a/home/zsh.nix b/home/zsh.nix index 32e1d61..0462b4b 100644 --- a/home/zsh.nix +++ b/home/zsh.nix @@ -23,8 +23,9 @@ enableZshIntegration = true; defaultOptions = [ "--color=dark" - "--color=fg:-1,bg:-1,hl:#c678dd,fg+:#ffffff,bg+:#4b5263,hl+:#d858fe" - "--color=info:#98c379,prompt:#61afef,pointer:#be5046,marker:#e5c07b,spinner:#61afef,header:#61afef" + "--color=fg:#e5e2e1,bg:-1,hl:#CFFC00,fg+:#ffffff,bg+:#1c1b1b,hl+:#e3ff66" + "--color=info:#ADAAAA,prompt:#CFFC00,pointer:#FF7A59,marker:#CFFC00,spinner:#00BFFF,header:#00BFFF" + "--color=border:#2a2929,bg+:-1,gutter:-1" ]; }; diff --git a/hosts/hades/default.nix b/hosts/hades/default.nix index 3b94652..e2e318c 100644 --- a/hosts/hades/default.nix +++ b/hosts/hades/default.nix @@ -19,6 +19,26 @@ networking.hostName = "hades"; + # --------------------------------------------------------------------------- + # Host display configuration (absorbed by home/hypr/hyprland.nix) + # --------------------------------------------------------------------------- + home-manager.users.anish.volt.monitors = [ + { + # Samsung Odyssey Ark - matched by description so it survives port changes + output = "desc:Samsung Electric Company Odyssey Ark H1AK500000"; + mode = "3840x2160@164.99Hz"; + position = "auto"; + scale = 1.25; + } + { + # Anything else plugged into hades: sensible automatic fallback + output = ""; + mode = "preferred"; + position = "auto"; + scale = "auto"; + } + ]; + # Enable in-memory compressed swap zramSwap.enable = true; diff --git a/modules/graphical/default.nix b/modules/graphical/default.nix index 0d48fcd..2b398e6 100644 --- a/modules/graphical/default.nix +++ b/modules/graphical/default.nix @@ -1,32 +1,41 @@ -{ pkgs, ... }: +{ pkgs, lib, ... }: { programs.hyprland = { enable = true; xwayland.enable = true; }; - - services.displayManager.sddm.enable = false; + services.getty.autologinUser = "anish"; - + + # Boot straight into Hyprland from the first virtual console on any machine. + # No display manager required; tty2-6 remain available as plain shells. + environment.loginShellInit = '' + if [ "$(tty)" = "/dev/tty1" ]; then + exec ${pkgs.hyprland}/bin/Hyprland + fi + ''; + + # Machine-agnostic Wayland session variables. + # GPU/vendor specific variables live with their hardware module (e.g. modules/hardware/nvidia.nix). environment.sessionVariables = { NIXOS_OZONE_WL = "1"; - WLR_NO_HARDWARE_CURSORS = "1"; - LIBVA_DRIVER_NAME = "nvidia"; - GBM_BACKEND = "nvidia-drm"; - __GLX_VENDOR_LIBRARY_NAME = "nvidia"; HYPRCURSOR_ENABLED = "1"; MOZ_ENABLE_WAYLAND = "1"; QT_QPA_PLATFORM = "wayland;xcb"; QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; CLUTTER_BACKEND = "wayland"; GDK_BACKEND = "wayland,x11,*"; - WLR_DRM_DEVICES = "/dev/dri/card1:/dev/dri/card0"; }; + # Fonts used across the desktop shell (Silicon Volt theme): + # JetBrains Mono for terminal/UI mono, IBM Plex Sans for UI sans, + # Instrument Serif for display italics, Nanum Gothic Coding as mono fallback. fonts.packages = with pkgs; [ nerd-fonts.jetbrains-mono nerd-fonts.fira-code font-awesome - ]; + ibm-plex + nanum-gothic-coding + ] ++ lib.optional (lib.hasAttr "instrument-serif" pkgs) pkgs.instrument-serif; } diff --git a/modules/hardware/nvidia.nix b/modules/hardware/nvidia.nix index 9cef640..b5cd275 100644 --- a/modules/hardware/nvidia.nix +++ b/modules/hardware/nvidia.nix @@ -22,4 +22,13 @@ nvidiaSettings = true; package = config.boot.kernelPackages.nvidiaPackages.stable; }; + + # NVIDIA-specific session variables live here so machines without + # NVIDIA GPUs (which do not import this module) stay vendor-clean. + environment.sessionVariables = { + LIBVA_DRIVER_NAME = "nvidia"; + GBM_BACKEND = "nvidia-drm"; + __GLX_VENDOR_LIBRARY_NAME = "nvidia"; + NVD_BACKEND = "direct"; + }; }