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