diff --git a/flake.nix b/flake.nix
index 3517e55..debdd1f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -75,6 +75,7 @@
modules = [
home-manager.nixosModules.home-manager
./modules/laptop.nix
+ ./modules/hyprland.nix
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
diff --git a/home/sscoble.nix b/home/sscoble.nix
index 0aca47e..3272c99 100644
--- a/home/sscoble.nix
+++ b/home/sscoble.nix
@@ -37,6 +37,44 @@
# File sync
rsync
rclone
+
+ # === Hyprland desktop ===
+ # Status bar
+ waybar
+ # App launcher
+ rofi-wayland
+ # Terminal
+ foot
+ # Terminal multiplexer (cmux equivalent for Linux)
+ zellij
+ # Notifications
+ dunst
+ libnotify # notify-send
+ # Wallpaper
+ swww
+ # Lock screen + idle
+ hyprlock
+ hypridle
+ # Screenshots
+ grim
+ slurp
+ swappy # annotation
+ # Clipboard
+ wl-clipboard
+ cliphist # clipboard history
+ # Brightness / audio control
+ brightnessctl
+ pavucontrol
+ # File manager
+ thunar
+ # Polkit agent (launched by systemd, but binary needed)
+ polkit_gnome
+ # Cursor theme
+ bibata-cursors
+ # Icons
+ papirus-icon-theme
+ # GTK theme
+ adw-gtk3
];
# === Git Config ===
@@ -102,6 +140,550 @@
# === Vault directory placeholder ===
home.file."vault/.gitkeep" = { text = ""; };
+ # ============================================================================
+ # Hyprland Desktop Configuration
+ # ============================================================================
+
+ # --- GTK / QT theming (applied to apps running under Hyprland)
+ gtk = {
+ enable = true;
+ theme.name = "adw-gtk3-dark";
+ iconTheme.name = "Papirus-Dark";
+ cursorTheme = {
+ name = "Bibata-Modern-Classic";
+ size = 24;
+ };
+ };
+ qt = {
+ enable = true;
+ platformTheme.name = "gtk";
+ style.name = "adwaita-dark";
+ };
+
+ # --- Hyprland WM config ---
+ home.file.".config/hypr/hyprland.conf".text = ''
+ # ██╗ ██╗██╗ ██╗██████╗ ██████╗ ██╗ █████╗ ███╗ ██╗██████╗
+ # ██║ ██║╚██╗ ██╔╝██╔══██╗██╔══██╗██║ ██╔══██╗████╗ ██║██╔══██╗
+ # ███████║ ╚████╔╝ ██████╔╝██████╔╝██║ ███████║██╔██╗ ██║██║ ██║
+ # ██╔══██║ ╚██╔╝ ██╔═══╝ ██╔══██╗██║ ██╔══██║██║╚██╗██║██║ ██║
+ # ██║ ██║ ██║ ██║ ██║ ██║███████╗██║ ██║██║ ╚████║██████╔╝
+ # ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝
+
+ # ============ MONITORS ============
+ monitor=,preferred,auto,1
+
+ # ============ ENVIRONMENT ============
+ env = XCURSOR_SIZE,24
+ env = XCURSOR_THEME,Bibata-Modern-Classic
+ env = GTK_THEME,adw-gtk3-dark
+ env = QT_QPA_PLATFORMTHEME,gtk2
+ env = LIBVA_DRIVER_NAME,iHD # Intel hardware acceleration
+
+ # ============ AUTOSTART ============
+ exec-once = waybar
+ exec-once = dunst
+ exec-once = swww-daemon
+ exec-once = swww img ~/wallpapers/default.jpg # create this dir, add a wallpaper
+ exec-once = wl-paste --type text --watch cliphist store # clipboard history
+ exec-once = wl-paste --type image --watch cliphist store
+ exec-once = hypridle
+ exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
+
+ # ============ INPUT ============
+ input {
+ kb_layout = us
+ kb_options = caps:escape
+ follow_mouse = 1
+ touchpad {
+ natural_scroll = true
+ tap-to-click = true
+ }
+ sensitivity = 0 # -1.0 to 1.0
+ }
+
+ # ============ GENERAL ============
+ general {
+ gaps_in = 6
+ gaps_out = 8
+ border_size = 2
+ col.active_border = rgba(8ebdffee) rgba(8ebdffff) rgba(8ebdffee) rgba(8ebdffff) 45deg
+ col.inactive_border = rgba(333333aa)
+ layout = dwindle
+ allow_tearing = false
+ }
+
+ # ============ DECORATION ============
+ decoration {
+ rounding = 8
+ blur {
+ enabled = true
+ size = 5
+ passes = 2
+ new_optimizations = true
+ }
+ drop_shadow = true
+ shadow_range = 12
+ shadow_render_power = 2
+ col.shadow = rgba(1a1a1aee)
+ }
+
+ # ============ ANIMATIONS ============
+ animations {
+ enabled = true
+ bezier = easeOutQuint,0.23,1,0.32,1
+ bezier = easeInOutCubic,0.65,0.05,0.36,1
+ animation = windows,1,7,easeOutQuint,slide
+ animation = windowsOut,1,7,easeInOutCubic,slide
+ animation = fade,1,7,easeOutQuint
+ animation = workspaces,1,4,easeOutQuint,fade
+ }
+
+ # ============ WORKSPACES ============
+ # Named workspaces — see waybar config for matching icons
+ workspace = 1, name:shell, persistent:true
+ workspace = 2, name:code, persistent:true
+ workspace = 3, name:web, persistent:true
+ workspace = 4, name:chat, persistent:true
+ workspace = 5, name:vault, persistent:true
+ workspace = 6, name:media
+ workspace = 7, name:7
+ workspace = 8, name:8
+ workspace = 9, name:9
+ workspace = 10, name:sys
+
+ # ============ KEYBINDS ============
+ # Mod key: SUPER (Windows key)
+ $mainMod = SUPER
+
+ # --- Launch ---
+ bind = $mainMod, Return, exec, foot
+ bind = $mainMod SHIFT, Return, exec, foot zellij
+ bind = $mainMod, B, exec, firefox
+ bind = $mainMod, D, exec, rofi -show drun -show-icons
+ bind = $mainMod, Space, exec, rofi -show drun -show-icons
+ bind = $mainMod, V, exec, cliphist list | rofi -dmenu | cliphist decode | wl-copy
+ bind = $mainMod, N, exec, thunar
+ bind = $mainMod, L, exec, hyprlock
+
+ # --- Window control ---
+ bind = $mainMod, Q, killactive,
+ bind = $mainMod, F, fullscreen
+ bind = $mainMod, G, togglefloating,
+ bind = $mainMod, P, pseudo, # dwindle split
+ bind = $mainMod, T, pin, # keep on top
+
+ # --- Focus ---
+ bind = $mainMod, H, movefocus, l
+ bind = $mainMod, J, movefocus, d
+ bind = $mainMod, K, movefocus, u
+ bind = $mainMod, L, movefocus, r
+ # Arrow keys alternative
+ bind = $mainMod, left, movefocus, l
+ bind = $mainMod, down, movefocus, d
+ bind = $mainMod, up, movefocus, u
+ bind = $mainMod, right, movefocus, r
+
+ # --- Move windows ---
+ bind = $mainMod SHIFT, H, movewindow, l
+ bind = $mainMod SHIFT, J, movewindow, d
+ bind = $mainMod SHIFT, K, movewindow, u
+ bind = $mainMod SHIFT, L, movewindow, r
+
+ # --- Resize ---
+ bind = $mainMod CTRL, H, resizeactive, -40 0
+ bind = $mainMod CTRL, J, resizeactive, 0 40
+ bind = $mainMod CTRL, K, resizeactive, 0 -40
+ bind = $mainMod CTRL, L, resizeactive, 40 0
+
+ # --- Workspaces ---
+ bind = $mainMod, 1, workspace, name:shell
+ bind = $mainMod, 2, workspace, name:code
+ bind = $mainMod, 3, workspace, name:web
+ bind = $mainMod, 4, workspace, name:chat
+ bind = $mainMod, 5, workspace, name:vault
+ bind = $mainMod, 6, workspace, name:media
+ bind = $mainMod, 7, workspace, name:7
+ bind = $mainMod, 8, workspace, name:8
+ bind = $mainMod, 9, workspace, name:9
+ bind = $mainMod, 0, workspace, name:sys
+
+ # --- Move to workspace ---
+ bind = $mainMod SHIFT, 1, movetoworkspace, name:shell
+ bind = $mainMod SHIFT, 2, movetoworkspace, name:code
+ bind = $mainMod SHIFT, 3, movetoworkspace, name:web
+ bind = $mainMod SHIFT, 4, movetoworkspace, name:chat
+ bind = $mainMod SHIFT, 5, movetoworkspace, name:vault
+ bind = $mainMod SHIFT, 6, movetoworkspace, name:media
+ bind = $mainMod SHIFT, 7, movetoworkspace, name:7
+ bind = $mainMod SHIFT, 8, movetoworkspace, name:8
+ bind = $mainMod SHIFT, 9, movetoworkspace, name:9
+ bind = $mainMod SHIFT, 0, movetoworkspace, name:sys
+
+ # --- Scroll workspace (mouse) ---
+ bind = $mainMod, mouse_down, workspace, e+1
+ bind = $mainMod, mouse_up, workspace, e-1
+
+ # --- Screenshot ---
+ bind = $mainMod, S, exec, grim -g "$(slurp)" - | swappy -f -
+ bind = $mainMod SHIFT, S, exec, grim - | swappy -f -
+ bind = , Print, exec, grim -g "$(slurp)" - | wl-copy
+
+ # --- Media keys ---
+ bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
+ bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
+ bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
+ bindel = , XF86MonBrightnessUp, exec, brightnessctl set 5%+
+ bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%-
+
+ # --- Session ---
+ bind = $mainMod SHIFT, Q, exec, hyprctl dispatch exit
+ bind = $mainMod SHIFT, R, exec, hyprctl reload
+
+ # ============ WINDOW RULES ============
+ windowrulev2 = float, class:(pavucontrol)
+ windowrulev2 = float, class:(org.gnome.Nautilus), title:(Properties)
+ windowrulev2 = float, class:(firefox), title:(Picture-in-Picture)
+ windowrulev2 = float, title:(Authentication Required)
+ windowrulev2 = size 75% 75%,class:(pavucontrol)
+
+ # Send known apps to specific workspaces
+ windowrulev2 = workspace name:code, class:(code-oss|Code|jetbrains-.*|nvim)
+ windowrulev2 = workspace name:web, class:(firefox|chromium-browser|google-chrome)
+ windowrulev2 = workspace name:chat, class:(discord|Slack|TelegramDesktop)
+ windowrulev2 = workspace name:vault, class:(obsidian|qmd)
+ windowrulev2 = workspace name:shell, class:(foot|Zellij|Alacritty|ghostty)
+ '';
+
+ # --- Foot terminal config ---
+ home.file.".config/foot/foot.ini".text = ''
+ font = JetBrainsMono Nerd Font:size=13
+ letter-spacing = 0
+ line-height = 1.1
+ pad = 8x8
+ cursor-style = bar
+ shell = zsh
+
+ [colors]
+ # Nord
+ foreground = d8dee9
+ background = 2e3440
+ selection-foreground = 2e3440
+ selection-background = 88c0d0
+ regular0 = 3b4252
+ regular1 = bf616a
+ regular2 = a3be8c
+ regular3 = ebcb8b
+ regular4 = 81a1c1
+ regular5 = b48ead
+ regular6 = 88c0d0
+ regular7 = e5e9f0
+ bright0 = 4c566a
+ bright1 = bf616a
+ bright2 = a3be8c
+ bright3 = ebcb8b
+ bright4 = 81a1c1
+ bright5 = b48ead
+ bright6 = 8fbcbb
+ bright7 = eceff4
+
+ [csd]
+ size = 0
+ '';
+
+ # --- Zellij config ---
+ home.file.".config/zellij/config.kdl".text = ''
+ theme "catppuccin-mocha"
+ default_shell "zsh"
+ pane_frames false
+ scrollback_editor "nvim"
+
+ keybinds clear-defaults=true {
+ normal {
+ bind "Ctrl g" { SwitchToMode "locked"; }
+ }
+ locked {
+ bind "Ctrl g" { SwitchToMode "normal"; }
+ bind "Ctrl q" { CloseTab; }
+ bind "Ctrl p" { NewPane "down"; }
+ bind "Ctrl n" { NewPane "right"; }
+ bind "Ctrl h" { MoveFocusOrTab "left"; }
+ bind "Ctrl j" { MoveFocusOrTab "down"; }
+ bind "Ctrl k" { MoveFocusOrTab "up"; }
+ bind "Ctrl l" { MoveFocusOrTab "right"; }
+ bind "Ctrl t" { NewTab; }
+ bind "Ctrl x" { CloseFocus; }
+ bind "Ctrl f" { ToggleFloatingPanes; }
+ bind "Ctrl z" { ToggleFocusFullscreen; }
+ bind "Ctrl r" { SwitchToMode "renametab"; }
+ bind "Ctrl d" { Detach; }
+ }
+ renametab {
+ bind "Ctrl c" { SwitchToMode "locked"; }
+ bind "Esc" { SwitchToMode "locked"; }
+ }
+ }
+ '';
+
+ # --- Hyprlock config ---
+ home.file.".config/hypr/hyprlock.conf".text = ''
+ general {
+ disable_loading_bar = false
+ hide_cursor = true
+ grace = 0
+ no_fade_in = false
+ }
+ background {
+ path = screenshot
+ blur_passes = 3
+ blur_size = 10
+ color = rgba(0,0,0,0.6)
+ }
+ input-field {
+ size = 250, 50
+ position = 0, -200
+ monitor =
+ dots_center = true
+ fade_on_empty = false
+ placeholder_text = unlock
+ outer_color = rgba(8ebdff,0.8)
+ inner_color = rgba(30,30,46,0.8)
+ font_color = rgba(255,255,255,0.9)
+ }
+ label {
+ text = cmd[update:60] date +"%A, %B %d %H:%M"
+ position = 0, 200
+ monitor =
+ font_size = 32
+ font_family = JetBrains Mono Nerd Font
+ color = rgba(255,255,255,0.9)
+ }
+ '';
+
+ # --- Hypridle config ---
+ home.file.".config/hypr/hypridle.conf".text = ''
+ general {
+ lock_cmd = hyprlock
+ before_sleep_cmd = hyprlock
+ }
+ listener {
+ timeout = 300 # 5 min
+ on-timeout = hyprlock
+ }
+ listener {
+ timeout = 600 # 10 min
+ on-timeout = hyprctl dispatch dpms off
+ on-resume = hyprctl dispatch dpms on
+ }
+ '';
+
+ # --- Waybar config ---
+ home.file.".config/waybar/config.jsonc".text = builtins.toJSON {
+ layer = "top";
+ position = "top";
+ height = 32;
+ spacing = 4;
+ margin = "6 8 0 8";
+
+ modules-left = [
+ "hyprland/workspaces"
+ ];
+ modules-center = [
+ "clock"
+ ];
+ modules-right = [
+ "tray"
+ "pulseaudio"
+ "backlight"
+ "battery"
+ "network"
+ "cpu"
+ "memory"
+ ];
+
+ # Workspaces — icons match Hyprland names
+ "hyprland/workspaces" = {
+ format = "{icon}";
+ format-icons = {
+ shell = ""; # 1: terminal
+ code = ""; # 2: development
+ web = ""; # 3: browser
+ chat = ""; # 4: communication
+ vault = ""; # 5: knowledge
+ media = ""; # 6: media
+ "7" = ""; # 7
+ "8" = ""; # 8
+ "9" = ""; # 9
+ sys = ""; # 10: system
+ default = "";
+ };
+ persistent_workspaces = {
+ "1" = [ "shell" ];
+ "2" = [ "code" ];
+ "3" = [ "web" ];
+ "4" = [ "chat" ];
+ "5" = [ "vault" ];
+ };
+ };
+
+ clock = {
+ format = "{:%a %b %d %H:%M}";
+ tooltip-format = "{:%Y %B}\n{calendar}";
+ };
+
+ cpu = {
+ format = " {usage}%";
+ };
+ memory = {
+ format = " {}%";
+ };
+ backlight = {
+ format = "{icon} {percent}%";
+ format-icons = [ "" "" "" "" ];
+ };
+ battery = {
+ format = "{icon} {capacity}%";
+ format-icons = [ "" "" "" "" "" "" "" "" "" "" ];
+ format-charging = " {capacity}%";
+ };
+ network = {
+ format-wifi = " {signalStrength}%";
+ format-ethernet = "";
+ format-disconnected = "⚠";
+ tooltip-format = "{ifname} via {gwaddr}";
+ };
+ pulseaudio = {
+ format = "{icon} {volume}%";
+ format-bluetooth = "{icon} {volume}%";
+ format-muted = "";
+ format-icons.default = [ "" "" "" ];
+ on-click = "pavucontrol";
+ };
+ tray.spacing = 10;
+ };
+
+ # --- Waybar stylesheet ---
+ home.file.".config/waybar/style.css".text = ''
+ * { font-family: "JetBrainsMono Nerd Font", "Font Awesome 6 Free"; font-size: 13px; }
+ window#waybar {
+ background: rgba(30,30,46,0.85);
+ border-radius: 8px;
+ color: #cdd6f4;
+ }
+ #workspaces button {
+ padding: 0 6px;
+ color: #6c7086;
+ background: transparent;
+ border-radius: 4px;
+ }
+ #workspaces button.active { color: #cdd6f4; background: #45475a; }
+ #workspaces button:hover { color: #cdd6f4; background: #313244; }
+ #clock, #cpu, #memory, #backlight, #battery, #network, #pulseaudio, #tray {
+ padding: 0 10px;
+ color: #cdd6f4;
+ }
+ #cpu.warning { color: #f9e2af; }
+ #cpu.critical { color: #f38ba8; }
+ #memory.warning { color: #f9e2af; }
+ #memory.critical { color: #f38ba8; }
+ #battery.warning { color: #f9e2af; }
+ #battery.critical { color: #f38ba8; }
+ tooltip {
+ background: rgba(30,30,46,0.95);
+ border: 1px solid #45475a;
+ border-radius: 6px;
+ }
+ '';
+
+ # --- Rofi launcher ---
+ home.file.".config/rofi/config.rasi".text = ''
+ configuration {
+ modi: "drun,run,window";
+ show-icons: true;
+ icon-theme: "Papirus-Dark";
+ drun-display-format: "{name}";
+ font: "JetBrainsMono Nerd Font 13";
+ matching: "normal";
+ disable-history: false;
+ }
+ window {
+ width: 600px;
+ border-radius: 10px;
+ background-color: rgba(30,30,46,0.95);
+ border: 2px solid #89b4fa;
+ }
+ mainbox { padding: 8px; }
+ inputbar {
+ padding: 4px 8px;
+ background-color: rgba(49,50,68,0.8);
+ border-radius: 8px;
+ margin-bottom: 4px;
+ }
+ entry {
+ background: transparent;
+ text-color: #cdd6f4;
+ caret-color: #cdd6f4;
+ }
+ listview {
+ background: transparent;
+ lines: 8;
+ }
+ element {
+ padding: 4px 8px;
+ border-radius: 6px;
+ text-color: #cdd6f4;
+ }
+ element selected {
+ background-color: rgba(137,180,250,0.3);
+ text-color: #ffffff;
+ }
+ element-text { vertical-align: 0.5; }
+ '';
+
+ # --- Dunst notifications ---
+ home.file.".config/dunst/dunstrc".text = ''
+ [global]
+ monitor = 0
+ follow = mouse
+ width = 350
+ height = 150
+ offset = 10x50
+ origin = top-right
+ notification_limit = 5
+ indicate_hidden = yes
+ shrink = no
+ transparency = 0
+ separator_height = 2
+ padding = 12
+ horizontal_padding = 12
+ frame_width = 2
+ frame_color = "#89b4fa"
+ separator_color = frame
+ sort = yes
+ font = JetBrainsMono Nerd Font 11
+ corner_radius = 8
+ background = "#1e1e2e"
+ foreground = "#cdd6f4"
+ timeout = 5
+ idle_threshold = 120
+ icon_position = left
+ max_icon_size = 48
+ markup = full
+ format = "%s\n%b"
+
+ [urgency_low]
+ background = "#1e1e2e"
+ foreground = "#cdd6f4"
+ timeout = 3
+ [urgency_normal]
+ background = "#1e1e2e"
+ foreground = "#cdd6f4"
+ timeout = 5
+ [urgency_critical]
+ background = "#f38ba8"
+ foreground = "#1e1e2e"
+ timeout = 0
+ '';
+
# === Shell Direnv ===
programs.direnv = {
enable = true;
diff --git a/modules/hyprland.nix b/modules/hyprland.nix
new file mode 100644
index 0000000..9097bf0
--- /dev/null
+++ b/modules/hyprland.nix
@@ -0,0 +1,69 @@
+{ config, pkgs, lib, ... }:
+
+{
+ # === Wayland + Hyprland ===
+ programs.hyprland = {
+ enable = true;
+ xwayland.enable = true; # For X11 apps (Electron, etc.)
+ };
+
+ # Wayland essentials
+ environment.sessionVariables.NIXOS_OZONE_WL = "1"; # Force Wayland for Electron
+
+ # Security — required for hyprlock
+ security.pam.services.hyprlock = {};
+
+ # Polkit for GUI auth prompts
+ security.polkit.enable = true;
+ systemd.user.services.polkit-gnome-authentication-agent-1 = {
+ description = "polkit-gnome-authentication-agent-1";
+ wantedBy = [ "graphical-session.target" ];
+ wants = [ "graphical-session.target" ];
+ after = [ "graphical-session.target" ];
+ serviceConfig = {
+ Type = "simple";
+ ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1";
+ Restart = "on-failure";
+ RestartSec = 1;
+ };
+ };
+
+ # Display manager — greetd with tuigreet for lightweight login
+ services.greetd = {
+ enable = true;
+ settings = {
+ default_session = {
+ command = "${pkgs.greetd.tuigreet}/bin/tuigreet --time --remember --cmd Hyprland";
+ user = "greeter";
+ };
+ };
+ };
+
+ # XDG portal for Wayland (screensharing, file dialogs)
+ xdg.portal = {
+ enable = true;
+ extraPortals = [ pkgs.xdg-desktop-portal-hyprland ];
+ config.common.default = "*";
+ };
+
+ # Fonts for Waybar / desktop
+ fonts.packages = with pkgs; [
+ font-awesome
+ (nerdfonts.override { fonts = [ "JetBrainsMono" "FiraCode" ]; })
+ ];
+
+ # Audio (pipewire)
+ services.pipewire = {
+ enable = true;
+ alsa.enable = true;
+ alsa.support32Bit = true;
+ pulse.enable = true;
+ };
+
+ # Bluetooth
+ hardware.bluetooth.enable = true;
+ hardware.bluetooth.powerOnBoot = true;
+
+ # NetworkManager applet
+ programs.nm-applet.enable = true;
+}