diff --git a/Makefile b/Makefile index 9b9ad692d..215a4f223 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,8 @@ DOCKER_IMAGE_TAGGED := $(DOCKER_IMAGE_NAME_BASE):$(GIT_COMMIT_SHA) NIX_EXEC := $(shell which nix) # Common cache settings (only applied when user is trusted to avoid warnings) -NIX_SUBSTITUTERS := https://cache.nixos.org https://devenv.cachix.org https://cachix.cachix.org -NIX_TRUSTED_KEYS := cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw= cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM= +NIX_SUBSTITUTERS := https://cache.nixos.org https://devenv.cachix.org https://cachix.cachix.org https://hyprland.cachix.org +NIX_TRUSTED_KEYS := cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw= cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM= hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc= NIX_CACHIX_CONF := /etc/nix/cachix.conf # Check if user is trusted (to avoid "ignoring untrusted substituter" warnings) NIX_USER_TRUSTED := $(shell grep -qE "trusted-users.*=.*(\\*|$(shell whoami))" /etc/nix/nix.conf 2>/dev/null && echo "yes" || echo "no") @@ -749,7 +749,7 @@ launchctl-ollama: ## Restart ollama launchd agent. ##@ Systemd Services (Linux) .PHONY: systemctl -systemctl: systemctl-cliproxyapi systemctl-openclaw systemctl-code-syncer systemctl-docker-postgres systemctl-dotfiles-updater systemctl-ollama ## Restart all systemd user services. +systemctl: systemctl-cliproxyapi systemctl-code-syncer systemctl-docker-postgres systemctl-dotfiles-updater systemctl-ollama systemctl-openclaw systemctl-waybar ## Restart all systemd user services. .PHONY: systemctl-cliproxyapi systemctl-cliproxyapi: ## Pull latest image and restart cliproxyapi systemd user service. @@ -757,7 +757,6 @@ systemctl-cliproxyapi: ## Pull latest image and restart cliproxyapi systemd user @systemctl --user restart cliproxyapi.service || true @echo "โœ… cliproxyapi restarted" - .PHONY: systemctl-code-syncer systemctl-code-syncer: ## Restart code-syncer systemd user service. @echo "๐Ÿ”„ Restarting code-syncer..." @@ -788,6 +787,14 @@ systemctl-openclaw: ## Restart OpenClaw gateway systemd user service. @systemctl --user restart openclaw-gateway.service || true @echo "โœ… openclaw restarted" +.PHONY: systemctl-waybar +systemctl-waybar: ## Restart waybar. + @echo "๐Ÿ”„ Restarting waybar..." + @pkill waybar 2>/dev/null || true + @sleep 1 + @hyprctl dispatch exec waybar 2>/dev/null || true + @echo "โœ… waybar restarted" + ##@ Git Submodule .PHONY: git-submodule-sync diff --git a/config/codex/config.toml b/config/codex/config.toml index 6b436a083..5d982d044 100644 --- a/config/codex/config.toml +++ b/config/codex/config.toml @@ -7,6 +7,8 @@ notify = [ suppress_unstable_features_warning = true web_search = "cached" +model = "gpt-5.3-codex" +model_reasoning_effort = "medium" [features] ghost_commit = false diff --git a/config/default.nix b/config/default.nix index 093ad5335..131aca6f2 100644 --- a/config/default.nix +++ b/config/default.nix @@ -1,3 +1,7 @@ +{ inputs }: +let + inherit (inputs.host) isDesktop; +in [ ./aichat ./amp @@ -23,3 +27,16 @@ ./starship ./zellij ] +++ ( + if isDesktop then + [ + ./gtk + ./hyprland + ./swaync + ./waybar + ./wlogout + ./wofi + ] + else + [ ] +) diff --git a/config/ghostty/config b/config/ghostty/config index 02c8db8d8..d9ece2995 100644 --- a/config/ghostty/config +++ b/config/ghostty/config @@ -14,8 +14,12 @@ cursor-style = bar cursor-style-blink = true mouse-hide-while-typing = true clipboard-read = "allow" +clipboard-write = "allow" clipboard-paste-protection = false +copy-on-select = clipboard +keybind = ctrl+c=copy_to_clipboard +keybind = ctrl+v=paste_from_clipboard keybind = super+alt+left=previous_tab keybind = super+alt+right=next_tab keybind = super+shift+left=move_tab:-1 diff --git a/config/gtk/default.nix b/config/gtk/default.nix new file mode 100644 index 000000000..e0aac758a --- /dev/null +++ b/config/gtk/default.nix @@ -0,0 +1,35 @@ +{ + config, + pkgs, + lib, + ... +}: +{ + gtk = lib.mkIf pkgs.stdenv.isLinux { + enable = true; + theme = { + name = "Adwaita-dark"; + package = pkgs.gnome-themes-extra; + }; + iconTheme = { + name = "Adwaita"; + package = pkgs.adwaita-icon-theme; + }; + cursorTheme = { + name = "Adwaita"; + size = 24; + }; + gtk3.extraConfig = { + gtk-application-prefer-dark-theme = 1; + }; + gtk4.extraConfig = { + gtk-application-prefer-dark-theme = 1; + }; + }; + + dconf.settings = lib.mkIf pkgs.stdenv.isLinux { + "org/gnome/desktop/interface" = { + color-scheme = "prefer-dark"; + }; + }; +} diff --git a/config/hyprland/default.nix b/config/hyprland/default.nix new file mode 100644 index 000000000..d2279b80d --- /dev/null +++ b/config/hyprland/default.nix @@ -0,0 +1,29 @@ +{ + config, + inputs, + pkgs, + ... +}: +let + hyprexpoPlugin = pkgs.hyprlandPlugins.hyprexpo; +in +{ + wayland.windowManager.hyprland = { + enable = true; + package = pkgs.hyprland; + systemd.enable = false; + extraConfig = '' + plugin = ${hyprexpoPlugin}/lib/libhyprexpo.so + '' + + builtins.readFile ./hyprland.conf; + }; + + xdg.configFile."hypr/hypridle.conf" = { + source = ./hypridle.conf; + force = true; + }; + xdg.configFile."hypr/hyprlock.conf" = { + source = ./hyprlock.conf; + force = true; + }; +} diff --git a/config/hyprland/hypridle.conf b/config/hyprland/hypridle.conf new file mode 100644 index 000000000..9298810d8 --- /dev/null +++ b/config/hyprland/hypridle.conf @@ -0,0 +1,31 @@ +general { + lock_cmd = pidof hyprlock || hyprlock + before_sleep_cmd = loginctl lock-session + after_sleep_cmd = hyprctl dispatch dpms on +} + +# Dim screen after 2.5 minutes +listener { + timeout = 150 + on-timeout = brightnessctl -s set 10 + on-resume = brightnessctl -r +} + +# Lock screen after 5 minutes +listener { + timeout = 300 + on-timeout = loginctl lock-session +} + +# Turn off display after 5.5 minutes +listener { + timeout = 330 + on-timeout = hyprctl dispatch dpms off + on-resume = hyprctl dispatch dpms on +} + +# Suspend after 30 minutes +listener { + timeout = 1800 + on-timeout = systemctl suspend +} diff --git a/config/hyprland/hyprland.conf b/config/hyprland/hyprland.conf new file mode 100644 index 000000000..e91f6eab8 --- /dev/null +++ b/config/hyprland/hyprland.conf @@ -0,0 +1,314 @@ +# ============================================================================= +# Dracula Colors +# ============================================================================= +$background = rgb(282a36) +$currentline = rgb(44475a) +$foreground = rgb(f8f8f2) +$comment = rgb(6272a4) +$cyan = rgb(8be9fd) +$green = rgb(50fa7b) +$orange = rgb(ffb86c) +$pink = rgb(ff79c6) +$purple = rgb(bd93f9) +$red = rgb(ff5555) +$yellow = rgb(f1fa8c) + +# ============================================================================= +# Variables +# ============================================================================= +$mod = SUPER + +# ============================================================================= +# Environment +# ============================================================================= +env = XCURSOR_THEME,Adwaita +env = XCURSOR_SIZE,24 +env = GTK_THEME,Adwaita:dark + +# ============================================================================= +# Startup +# ============================================================================= +exec-once = dbus-update-activation-environment --systemd --all +exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE XDG_RUNTIME_DIR HYPRLAND_INSTANCE_SIGNATURE XDG_SESSION_DESKTOP + +# Status bar +exec-once = waybar + +# Notification daemon +exec-once = swaync + +# Clipboard history +exec-once = wl-paste --type text --watch cliphist store +exec-once = wl-paste --type image --watch cliphist store + +# Idle management +exec-once = hypridle + +# Volume/brightness OSD +exec-once = rm -f /tmp/wobpipe && mkfifo /tmp/wobpipe && tail -f /tmp/wobpipe | wob + +# ============================================================================= +# Monitor (Framework 13" 2256x1504) +# ============================================================================= +monitor = , preferred, auto, 1.5 + +# ============================================================================= +# Input +# ============================================================================= +input { + kb_layout = us + follow_mouse = 1 + sensitivity = 0 + + touchpad { + natural_scroll = true + tap-to-click = true + drag_lock = true + } +} + +gesture = 4, horizontal, workspace + +# ============================================================================= +# General +# ============================================================================= +general { + gaps_in = 4 + gaps_out = 8 + border_size = 3 + col.active_border = $purple $pink 45deg + col.inactive_border = $currentline + layout = dwindle + allow_tearing = false + resize_on_border = true +} + +dwindle { + pseudotile = true + preserve_split = true +} + +# ============================================================================= +# Decoration +# ============================================================================= +decoration { + rounding = 8 + + blur { + enabled = true + size = 5 + passes = 3 + new_optimizations = true + xray = true + } + + shadow { + enabled = true + range = 15 + render_power = 3 + color = $background + } +} + +# ============================================================================= +# Animations +# ============================================================================= +animations { + enabled = true + + bezier = snap, 0.2, 1.0, 0.3, 1.0 + bezier = liner, 1, 1, 1, 1 + + animation = windows, 1, 2, snap, slide + animation = windowsIn, 1, 2, snap, slide + animation = windowsOut, 1, 2, snap, slide + animation = windowsMove, 1, 2, snap, slide + animation = border, 1, 1, liner + animation = borderangle, 1, 30, liner, loop + animation = fade, 1, 3, default + animation = workspaces, 1, 3, snap +} + +# ============================================================================= +# Misc +# ============================================================================= +misc { + disable_hyprland_logo = true + disable_splash_rendering = true + force_default_wallpaper = 0 +} + +# ============================================================================= +# Window Rules +# ============================================================================= +windowrule = float on, match:class ^(wofi)$ +windowrule = float on, match:class ^(1Password)$ +windowrule = float on, match:title ^(Picture-in-Picture)$ +windowrule = pin on, match:title ^(Picture-in-Picture)$ + +# Notification center blur +layerrule = blur on, match:namespace swaync-control-center +layerrule = blur on, match:namespace swaync-notification-window +layerrule = ignore_alpha 0.0, match:namespace swaync-control-center +layerrule = ignore_alpha 0.0, match:namespace swaync-notification-window + +# Prevent wofi animation +layerrule = animation slide, match:namespace ^(wofi)$ + +# ============================================================================= +# App Launch Hotkeys (Super + letter) +# ============================================================================= +bind = $mod, T, exec, ghostty +bind = $mod, G, exec, google-chrome-stable +bind = $mod, S, exec, slack +bind = $mod, C, exec, cursor +bind = $mod, P, exec, gtk-launch 1password +bind = $mod, M, exec, gtk-launch signal-desktop +bind = $mod, N, exec, ghostty -e yazi + +# ============================================================================= +# Launcher & Clipboard +# ============================================================================= +bind = $mod, V, exec, code +bind = $mod, D, exec, wofi --show drun +bind = $mod, E, exec, wofi-emoji +bind = $mod SHIFT, V, exec, cliphist list | wofi --dmenu | cliphist decode | wl-copy + +# ============================================================================= +# Window Management +# ============================================================================= +bind = $mod, W, killactive, +bind = $mod, F, togglefloating, +bind = CTRL ALT SHIFT, F, exec, hyprctl --batch "dispatch movetoworkspace empty; dispatch fullscreen 0" +bind = SUPER CTRL, F, fullscreen, 0 +bind = $mod SHIFT, Q, exec, wlogout +bind = $mod, TAB, hyprexpo:expo, toggle + +# Focus (vim keys) +bind = $mod, H, movefocus, l +bind = $mod, L, movefocus, r +bind = $mod, K, movefocus, u +bind = $mod, J, movefocus, d + +# Focus (arrow keys) +bind = $mod, left, movefocus, l +bind = $mod, right, movefocus, r +bind = $mod, up, movefocus, u +bind = $mod, down, movefocus, d + +# Move windows (vim keys) +bind = $mod SHIFT, H, movewindow, l +bind = $mod SHIFT, L, movewindow, r +bind = $mod SHIFT, K, movewindow, u +bind = $mod SHIFT, J, movewindow, d + +# Move windows (arrow keys) +bind = $mod SHIFT, left, movewindow, l +bind = $mod SHIFT, right, movewindow, r +bind = $mod SHIFT, up, movewindow, u +bind = $mod SHIFT, down, movewindow, d + +# Resize windows +binde = $mod CTRL, left, resizeactive, -20 0 +binde = $mod CTRL, right, resizeactive, 20 0 +binde = $mod CTRL, up, resizeactive, 0 -20 +binde = $mod CTRL, down, resizeactive, 0 20 + +# ============================================================================= +# Workspaces +# ============================================================================= +bind = $mod, 1, workspace, 1 +bind = $mod, 2, workspace, 2 +bind = $mod, 3, workspace, 3 +bind = $mod, 4, workspace, 4 +bind = $mod, 5, workspace, 5 +bind = $mod, 6, workspace, 6 +bind = $mod, 7, workspace, 7 +bind = $mod, 8, workspace, 8 +bind = $mod, 9, workspace, 9 + +bind = $mod SHIFT, 1, movetoworkspace, 1 +bind = $mod SHIFT, 2, movetoworkspace, 2 +bind = $mod SHIFT, 3, movetoworkspace, 3 +bind = $mod SHIFT, 4, movetoworkspace, 4 +bind = $mod SHIFT, 5, movetoworkspace, 5 +bind = $mod SHIFT, 6, movetoworkspace, 6 +bind = $mod SHIFT, 7, movetoworkspace, 7 +bind = $mod SHIFT, 8, movetoworkspace, 8 +bind = $mod SHIFT, 9, movetoworkspace, 9 + +# Scroll through workspaces +bind = $mod, mouse_down, workspace, e+1 +bind = $mod, mouse_up, workspace, e-1 + +# Mouse window management +bindm = $mod, mouse:272, movewindow +bindm = $mod, mouse:273, resizewindow + +# ============================================================================= +# Screenshot +# ============================================================================= +bind = $mod SHIFT, S, exec, grim -g "$(slurp)" - | swappy -f - + +# ============================================================================= +# Media / Hardware Keys (Framework 13 AI 300) +# ============================================================================= +# F1: Mute speaker +bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle +bindl = , F1, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle +# F2: Volume down +bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- +bindel = , F2, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- +# F3: Volume up +bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+ +bindel = , F3, exec, wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+ +# F4: Previous track +bindl = , XF86AudioPrev, exec, playerctl previous +bindl = , F4, exec, playerctl previous +# F5: Next track +bindl = , XF86AudioNext, exec, playerctl next +bindl = , F5, exec, playerctl next +# F6: Play/pause +bindl = , XF86AudioPlay, exec, playerctl play-pause +bindl = , XF86AudioPause, exec, playerctl play-pause +bindl = , F6, exec, playerctl play-pause +# F7: Brightness down +bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%- +bindel = , F7, exec, brightnessctl set 5%- +# F8: Brightness up +bindel = , XF86MonBrightnessUp, exec, brightnessctl set 5%+ +bindel = , F8, exec, brightnessctl set 5%+ +# F9: Toggle display scale (1.0x <-> 1.5x) +bindl = , XF86Display, exec, hyprctl -j monitors | grep -q '"scale": 1.5' && hyprctl keyword monitor "eDP-1, preferred, auto, 1.0" || hyprctl keyword monitor "eDP-1, preferred, auto, 1.5" +bindl = , F9, exec, hyprctl -j monitors | grep -q '"scale": 1.5' && hyprctl keyword monitor "eDP-1, preferred, auto, 1.0" || hyprctl keyword monitor "eDP-1, preferred, auto, 1.5" +# F10: Airplane mode +bindl = , XF86RFKill, exec, nmcli radio wifi toggle +bindl = , F10, exec, nmcli radio wifi toggle +# F11: Print screen +bind = , Print, exec, grim -g "$(slurp)" - | swappy -f - +bind = , F11, exec, grim -g "$(slurp)" - | swappy -f - + +# ============================================================================= +# Lock Screen +# ============================================================================= +bind = $mod CTRL, L, exec, hyprlock + +# ============================================================================= +# Notification Toggle +# ============================================================================= +bind = $mod SHIFT, D, exec, swaync-client -t -sw + +# ============================================================================= +# Plugins +# ============================================================================= +plugin { + hyprexpo { + columns = 3 + gap_size = 5 + bg_col = rgb(282a36) + workspace_method = first 1 + gesture_fingers = 3 + gesture_distance = 300 + gesture_positive = false + } +} diff --git a/config/hyprland/hyprlock.conf b/config/hyprland/hyprlock.conf new file mode 100644 index 000000000..9edc57e7c --- /dev/null +++ b/config/hyprland/hyprlock.conf @@ -0,0 +1,38 @@ +background { + monitor = + path = screenshot + blur_passes = 3 + blur_size = 8 + contrast = 0.8 + brightness = 0.7 + vibrancy = 0.17 +} + +input-field { + monitor = + size = 250, 50 + outline_thickness = 3 + dots_size = 0.26 + dots_spacing = 0.15 + dots_center = true + outer_color = rgb(bd93f9) + inner_color = rgb(282a36) + font_color = rgb(f8f8f2) + fade_on_empty = true + placeholder_text = Password... + hide_input = false + position = 0, -20 + halign = center + valign = center +} + +label { + monitor = + text = $TIME + color = rgb(f8f8f2) + font_size = 64 + font_family = JetBrainsMono Nerd Font + position = 0, 80 + halign = center + valign = center +} diff --git a/config/keyd/default.conf b/config/keyd/default.conf new file mode 100644 index 000000000..e38901bb3 --- /dev/null +++ b/config/keyd/default.conf @@ -0,0 +1,33 @@ +[ids] +* + +[main] +# Caps Lock = pure Super (for Hyprland hotkeys) +capslock = layer(meta) + +# Right Alt = pure Super (right-hand mirror of Caps Lock) +rightalt = layer(meta) + +# Framework key = macOS-style Command (copy/paste/undo) +# leftmeta is the actual keycode on Framework 13 AI 300 +leftmeta = layer(cmd_mac) +prog1 = layer(cmd_mac) +f13 = layer(cmd_mac) + +# Right Shift double-tap -> Caps Lock (tap twice) +rightshift = overload(shift, oneshot(rshift)) + +[rshift] +rightshift = capslock + +[cmd_mac] +# macOS-style shortcuts via Ctrl +c = C-c +v = C-v +x = C-x +z = C-z +a = C-a + +[cmd_mac+control] +# Framework+Ctrl+F โ†’ Ctrl+Alt+Shift+F (fullscreen in Hyprland) +f = C-A-S-f diff --git a/config/keyd/default.nix b/config/keyd/default.nix new file mode 100644 index 000000000..3bdae96c6 --- /dev/null +++ b/config/keyd/default.nix @@ -0,0 +1,18 @@ +{ ... }: +{ + services.keyd.enable = true; + + # Optional: silence the setgid warning (nice to have, not required for functionality) + users.groups.keyd = { }; + systemd.services.keyd.serviceConfig = { + CapabilityBoundingSet = [ "CAP_SETGID" ]; + AmbientCapabilities = [ "CAP_SETGID" ]; + }; + + # Restart keyd when config changes (on make switch) + systemd.services.keyd.restartTriggers = [ + (builtins.hashFile "sha256" ./default.conf) + ]; + + environment.etc."keyd/default.conf".source = ./default.conf; +} diff --git a/config/swaync/config.json b/config/swaync/config.json new file mode 100644 index 000000000..d9d6b1c26 --- /dev/null +++ b/config/swaync/config.json @@ -0,0 +1,34 @@ +{ + "$schema": "/etc/xdg/swaync/configSchema.json", + "positionX": "right", + "positionY": "top", + "layer": "overlay", + "control-center-width": 420, + "control-center-height": 600, + "notification-window-width": 380, + "notification-icon-size": 48, + "notification-body-image-height": 100, + "notification-body-image-width": 200, + "timeout": 5, + "timeout-low": 3, + "timeout-critical": 0, + "fit-to-screen": true, + "relative-timestamps": true, + "hide-on-action": true, + "hide-on-clear": true, + "widgets": [ + "title", + "dnd", + "notifications" + ], + "widget-config": { + "title": { + "text": "Notifications", + "clear-all-button": true, + "button-text": "Clear" + }, + "dnd": { + "text": "Do Not Disturb" + } + } +} diff --git a/config/swaync/default.nix b/config/swaync/default.nix new file mode 100644 index 000000000..0bf0a28d0 --- /dev/null +++ b/config/swaync/default.nix @@ -0,0 +1,11 @@ +{ config, ... }: +{ + xdg.configFile."swaync/config.json" = { + source = ./config.json; + force = true; + }; + xdg.configFile."swaync/style.css" = { + source = ./style.css; + force = true; + }; +} diff --git a/config/swaync/style.css b/config/swaync/style.css new file mode 100644 index 000000000..9e09f66bb --- /dev/null +++ b/config/swaync/style.css @@ -0,0 +1,119 @@ +.notification-row { + outline: none; +} + +.notification-row:focus, +.notification-row:hover { + background: #44475a; +} + +.notification { + border-radius: 12px; + margin: 4px 8px; + padding: 0; + border: 2px solid #44475a; + background: #282a36; +} + +.notification-content { + padding: 8px; +} + +.close-button { + background: #ff5555; + color: #282a36; + border-radius: 50%; + margin: 4px; + padding: 2px; + min-width: 20px; + min-height: 20px; +} + +.close-button:hover { + background: #ff79c6; +} + +.notification-default-action, +.notification-action { + border-radius: 8px; + margin: 4px; + padding: 4px 8px; + background: #44475a; + color: #f8f8f2; + border: none; +} + +.notification-default-action:hover, +.notification-action:hover { + background: #6272a4; +} + +.summary { + font-weight: bold; + font-size: 14px; + color: #f8f8f2; +} + +.body { + font-size: 13px; + color: #f8f8f2; +} + +.time { + font-size: 11px; + color: #6272a4; + margin-right: 8px; +} + +.control-center { + background: #282a36; + border: 2px solid #44475a; + border-radius: 12px; + margin: 8px; + padding: 8px; +} + +.control-center-list { + background: transparent; +} + +.widget-title { + color: #f8f8f2; + font-size: 16px; + font-weight: bold; + margin: 8px; +} + +.widget-title > button { + background: #bd93f9; + color: #282a36; + border-radius: 8px; + padding: 4px 12px; + border: none; + font-weight: bold; +} + +.widget-title > button:hover { + background: #ff79c6; +} + +.widget-dnd { + color: #f8f8f2; + margin: 4px 8px; + font-size: 14px; +} + +.widget-dnd > switch { + background: #44475a; + border-radius: 12px; + border: 2px solid #6272a4; +} + +.widget-dnd > switch:checked { + background: #bd93f9; +} + +.widget-dnd > switch slider { + background: #f8f8f2; + border-radius: 50%; +} diff --git a/config/waybar/config.jsonc b/config/waybar/config.jsonc new file mode 100644 index 000000000..726855004 --- /dev/null +++ b/config/waybar/config.jsonc @@ -0,0 +1,124 @@ +{ + "layer": "top", + "position": "top", + "height": 36, + "spacing": 4, + + "modules-left": ["hyprland/workspaces"], + "modules-center": ["clock"], + "modules-right": [ + "cpu", + "memory", + "pulseaudio", + "backlight", + "battery", + "network", + "bluetooth", + "tray", + "custom/notification" + ], + + "hyprland/workspaces": { + "format": "{id}", + "on-click": "activate", + "sort-by-number": true + }, + + "clock": { + "interval": 1, + "format": "{:%Y/%m/%d %H:%M:%S}", + "tooltip-format": "{calendar}", + "calendar": { + "mode": "month", + "weeks-pos": "left", + "format": { + "months": "{}", + "weeks": "W{}", + "weekdays": "{}", + "today": "{}" + } + } + }, + + "cpu": { + "interval": 5, + "format": " {usage}%", + "tooltip-format": "{avg_frequency} GHz\n{usage}% used" + }, + + "memory": { + "interval": 5, + "format": " {percentage}%", + "tooltip-format": "{used:0.1f} / {total:0.1f} GiB" + }, + + "pulseaudio": { + "format": "{icon} {volume}%", + "format-muted": "๓ฐŸ muted", + "format-icons": { + "default": ["๓ฐ•ฟ", "๓ฐ–€", "๓ฐ•พ"] + }, + "on-click": "pavucontrol", + "scroll-step": 5, + "tooltip": true + }, + + "backlight": { + "format": "{icon} {percent}%", + "format-icons": ["๓ฐƒž", "๓ฐƒŸ", "๓ฐƒ "], + "tooltip": true + }, + + "battery": { + "interval": 30, + "states": { + "warning": 30, + "critical": 15 + }, + "format": "{icon} {capacity}%", + "format-charging": "๓ฐ‚„ {capacity}%", + "format-plugged": "๓ฐ‚„ {capacity}%", + "format-icons": ["๓ฐ‚Ž", "๓ฐบ", "๓ฐป", "๓ฐผ", "๓ฐฝ", "๓ฐพ", "๓ฐฟ", "๓ฐ‚€", "๓ฐ‚", "๓ฐ‚‚", "๓ฐน"], + "tooltip-format": "{timeTo}\n{capacity}% - {power}W" + }, + + "network": { + "format-wifi": "๓ฐคจ {essid}", + "format-ethernet": "๓ฐˆ€ {ipaddr}", + "format-disconnected": "๓ฐคญ disconnected", + "tooltip-format-wifi": "{essid} ({signalStrength}%)\n{ipaddr}", + "tooltip-format-ethernet": "{ifname}\n{ipaddr}", + "on-click": "nm-connection-editor" + }, + + "bluetooth": { + "format": "๓ฐ‚ฏ", + "format-connected": "๓ฐ‚ฑ {num_connections}", + "format-disabled": "๓ฐ‚ฒ", + "tooltip-format": "{status}\n{device_enumerate}", + "tooltip-format-enumerate-connected": "{device_alias}", + "on-click": "blueman-manager" + }, + + "tray": { + "icon-size": 16, + "spacing": 8 + }, + + "custom/notification": { + "tooltip": false, + "format": "{icon}", + "format-icons": { + "notification": "๓ฐ‚š", + "none": "๓ฐ‚œ", + "dnd-notification": "๓ฐ‚›", + "dnd-none": "๓ฐช‘" + }, + "return-type": "json", + "exec-if": "which swaync-client", + "exec": "swaync-client -swb", + "on-click": "swaync-client -t -sw", + "on-click-right": "swaync-client -d -sw", + "escape": true + } +} diff --git a/config/waybar/default.nix b/config/waybar/default.nix new file mode 100644 index 000000000..88d5c2824 --- /dev/null +++ b/config/waybar/default.nix @@ -0,0 +1,11 @@ +{ config, ... }: +{ + xdg.configFile."waybar/config.jsonc" = { + source = ./config.jsonc; + force = true; + }; + xdg.configFile."waybar/style.css" = { + source = ./style.css; + force = true; + }; +} diff --git a/config/waybar/style.css b/config/waybar/style.css new file mode 100644 index 000000000..6e6b66a39 --- /dev/null +++ b/config/waybar/style.css @@ -0,0 +1,143 @@ +/* Dracula */ +@define-color background #282a36; +@define-color currentline #44475a; +@define-color foreground #f8f8f2; +@define-color comment #6272a4; +@define-color cyan #8be9fd; +@define-color green #50fa7b; +@define-color orange #ffb86c; +@define-color pink #ff79c6; +@define-color purple #bd93f9; +@define-color red #ff5555; +@define-color yellow #f1fa8c; + +* { + font-family: "JetBrainsMono Nerd Font", monospace; + font-size: 13px; + min-height: 0; +} + +window#waybar { + background-color: @background; + color: @foreground; + border-bottom: 2px solid @currentline; +} + +tooltip { + background-color: @background; + border: 1px solid @comment; + border-radius: 8px; + color: @foreground; +} + +#workspaces button { + padding: 0 8px; + color: @comment; + border-radius: 6px; + margin: 3px 2px; + background-color: transparent; +} + +#workspaces button.active { + color: @background; + background-color: @purple; +} + +#workspaces button:hover { + background-color: @currentline; + color: @foreground; +} + +#clock { + color: @foreground; + font-weight: bold; +} + +#cpu { + color: @orange; +} + +#memory { + color: @green; +} + +#pulseaudio { + color: @cyan; +} + +#pulseaudio.muted { + color: @comment; +} + +#backlight { + color: @yellow; +} + +#battery { + color: @green; +} + +#battery.warning { + color: @yellow; +} + +#battery.critical { + color: @red; + animation: blink 1s linear infinite; +} + +@keyframes blink { + to { + color: @background; + background-color: @red; + } +} + +#battery.charging { + color: @green; +} + +#network { + color: @cyan; +} + +#network.disconnected { + color: @comment; +} + +#bluetooth { + color: @purple; +} + +#bluetooth.disabled { + color: @comment; +} + +#tray { + color: @foreground; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#custom-notification { + color: @orange; + padding: 0 6px; +} + +/* Module spacing */ +#cpu, +#memory, +#pulseaudio, +#backlight, +#battery, +#network, +#bluetooth, +#tray, +#clock, +#custom-notification { + padding: 0 10px; + margin: 3px 0; + border-radius: 6px; +} diff --git a/config/wlogout/default.nix b/config/wlogout/default.nix new file mode 100644 index 000000000..31e3f5204 --- /dev/null +++ b/config/wlogout/default.nix @@ -0,0 +1,11 @@ +{ config, ... }: +{ + xdg.configFile."wlogout/layout" = { + source = ./layout; + force = true; + }; + xdg.configFile."wlogout/style.css" = { + source = ./style.css; + force = true; + }; +} diff --git a/config/wlogout/layout b/config/wlogout/layout new file mode 100644 index 000000000..bcb426522 --- /dev/null +++ b/config/wlogout/layout @@ -0,0 +1,30 @@ +{ + "label" : "lock", + "action" : "hyprlock", + "text" : "Lock", + "keybind" : "l" +} +{ + "label" : "logout", + "action" : "hyprctl dispatch exit", + "text" : "Logout", + "keybind" : "e" +} +{ + "label" : "suspend", + "action" : "systemctl suspend", + "text" : "Suspend", + "keybind" : "s" +} +{ + "label" : "reboot", + "action" : "systemctl reboot", + "text" : "Reboot", + "keybind" : "r" +} +{ + "label" : "shutdown", + "action" : "systemctl poweroff", + "text" : "Shutdown", + "keybind" : "p" +} diff --git a/config/wlogout/style.css b/config/wlogout/style.css new file mode 100644 index 000000000..86cee1020 --- /dev/null +++ b/config/wlogout/style.css @@ -0,0 +1,49 @@ +window { + background-color: rgba(40, 42, 54, 0.9); + font-family: "JetBrainsMono Nerd Font", monospace; +} + +button { + background-color: #44475a; + border-radius: 12px; + margin: 10px; + padding: 20px; + color: #f8f8f2; + font-size: 18px; + border: 2px solid #6272a4; + background-repeat: no-repeat; + background-position: center; + background-size: 48px; +} + +button:focus, +button:hover { + background-color: #6272a4; + border-color: #bd93f9; + outline: none; +} + +button:active { + background-color: #bd93f9; + color: #282a36; +} + +#lock { + background-image: image(url("/usr/share/wlogout/icons/lock.png")); +} + +#logout { + background-image: image(url("/usr/share/wlogout/icons/logout.png")); +} + +#suspend { + background-image: image(url("/usr/share/wlogout/icons/suspend.png")); +} + +#reboot { + background-image: image(url("/usr/share/wlogout/icons/reboot.png")); +} + +#shutdown { + background-image: image(url("/usr/share/wlogout/icons/shutdown.png")); +} diff --git a/config/wofi/config b/config/wofi/config new file mode 100644 index 000000000..678ed7bd7 --- /dev/null +++ b/config/wofi/config @@ -0,0 +1,15 @@ +width=600 +height=400 +location=center +show=drun +prompt=Search... +filter_rate=100 +allow_markup=true +no_actions=true +halign=fill +orientation=vertical +content_halign=fill +insensitive=true +allow_images=true +image_size=24 +layer=overlay diff --git a/config/wofi/default.nix b/config/wofi/default.nix new file mode 100644 index 000000000..11116bbfb --- /dev/null +++ b/config/wofi/default.nix @@ -0,0 +1,11 @@ +{ config, ... }: +{ + xdg.configFile."wofi/config" = { + source = ./config; + force = true; + }; + xdg.configFile."wofi/style.css" = { + source = ./style.css; + force = true; + }; +} diff --git a/config/wofi/style.css b/config/wofi/style.css new file mode 100644 index 000000000..45c29b8fc --- /dev/null +++ b/config/wofi/style.css @@ -0,0 +1,58 @@ +window { + margin: 0; + border: 2px solid #bd93f9; + border-radius: 12px; + background-color: #282a36; + font-family: "JetBrainsMono Nerd Font", monospace; + font-size: 14px; +} + +#input { + margin: 8px; + padding: 8px 12px; + border: none; + border-radius: 8px; + background-color: #44475a; + color: #f8f8f2; + font-size: 14px; +} + +#input:focus { + border: 2px solid #bd93f9; +} + +#inner-box { + margin: 4px 8px; +} + +#outer-box { + margin: 0; + padding: 0; +} + +#scroll { + margin: 0; +} + +#text { + color: #f8f8f2; + margin: 0 4px; +} + +#entry { + padding: 6px 8px; + border-radius: 8px; +} + +#entry:selected { + background-color: #bd93f9; + color: #282a36; +} + +#entry:hover { + background-color: #44475a; +} + +#img { + margin-right: 8px; +} diff --git a/flake.lock b/flake.lock index cf8cb8bd8..a5791b8cc 100644 --- a/flake.lock +++ b/flake.lock @@ -12,11 +12,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1762618334, - "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", + "lastModified": 1770165109, + "narHash": "sha256-9VnK6Oqai65puVJ4WYtCTvlJeXxMzAp/69HhQuTdl/I=", "owner": "ryantm", "repo": "agenix", - "rev": "fcdea223397448d35d9b31f798479227e80183f6", + "rev": "b027ee29d959fda4b60b57566d64c98a202e0feb", "type": "github" }, "original": { @@ -93,11 +93,11 @@ ] }, "locked": { - "lastModified": 1769723052, - "narHash": "sha256-pVUZ6DoKfIh6RhhTS2GVUukNeDm6qjXM7/hoUrr0x2Q=", + "lastModified": 1770390537, + "narHash": "sha256-REdgcXkdHHsLMNhPbTP1laBaSX5MAgWnuD9dou3CoQU=", "owner": "cachix", "repo": "devenv", - "rev": "12c672b3ec4e2cfe189636e9200bc70f7a478781", + "rev": "d6f45cc00829254a9a6f8807c8fbfaf3efa7e629", "type": "github" }, "original": { @@ -148,11 +148,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1768135262, - "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", "type": "github" }, "original": { @@ -169,11 +169,11 @@ ] }, "locked": { - "lastModified": 1768135262, - "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "lastModified": 1769996383, + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", "type": "github" }, "original": { @@ -241,11 +241,11 @@ ] }, "locked": { - "lastModified": 1769671018, - "narHash": "sha256-F70HHQtEVwpM14/DlFkyTA20sXtWMNVfqDoLLCm4BVI=", + "lastModified": 1770362352, + "narHash": "sha256-M1TbOR4PFVrnMYsxrObK9N8ZVOriMoMAwifx994bBqc=", "owner": "shazow", "repo": "foundry.nix", - "rev": "f843a7245c3cbdf3364c640198039a4186c30444", + "rev": "506da7b70d32763ad022f452907c6f39b1035a66", "type": "github" }, "original": { @@ -309,11 +309,11 @@ ] }, "locked": { - "lastModified": 1769723138, - "narHash": "sha256-kgkwjs33YfJasADIrHjHcTIDs3wNX0xzJhnUP+oldEw=", + "lastModified": 1770318660, + "narHash": "sha256-yFVde8QZK7Dc0Xa8eQDsmxLX4NJNfL1NKfctSyiQgMY=", "owner": "nix-community", "repo": "home-manager", - "rev": "175532b6275b34598a0ceb1aef4b9b4006dd4073", + "rev": "471e6a065f9efed51488d7c51a9abbd387df91b8", "type": "github" }, "original": { @@ -346,11 +346,11 @@ ] }, "locked": { - "lastModified": 1769731486, - "narHash": "sha256-FubWXQX1k1qyVKWiGNVSw2bQ2TD739PRqRsLXS/b2y0=", + "lastModified": 1770336287, + "narHash": "sha256-czvrg8uyf2VWRmbobsthTAIJCg1GH4mEekyW01AvHco=", "owner": "nix-community", "repo": "neovim-nightly-overlay", - "rev": "0d454d45604a3215057806757beda9bc7390d59e", + "rev": "1cd999cdf20536ac6a6d1aa17ba0242eefd2312b", "type": "github" }, "original": { @@ -362,11 +362,11 @@ "neovim-src": { "flake": false, "locked": { - "lastModified": 1769723720, - "narHash": "sha256-gmlHBj1nGfOBaCMEfgHdy/4sbuBXDaVIWNGJVPP+vNw=", + "lastModified": 1770334851, + "narHash": "sha256-FvT3T0l8eNr1Hv+D1Sj1jM/2vLkonLxpadTk6gdYHAo=", "owner": "neovim", "repo": "neovim", - "rev": "52cd9dcff260decda860cf154873b298d10c1208", + "rev": "db133879b2a115cdf982b2899f154f1851d59a60", "type": "github" }, "original": { @@ -401,11 +401,11 @@ ] }, "locked": { - "lastModified": 1769197468, - "narHash": "sha256-EhbVSjqhjykjIzF1FetpEwxjSMXg4ubHpkuEnE0y23A=", + "lastModified": 1770388968, + "narHash": "sha256-i7V6Iz9MZXZntMsJTFV3udH4Cx5NPkiETbhEBPMZEec=", "owner": "cachix", "repo": "nix", - "rev": "6174571301a92afce9b7296d5babdc56972695b7", + "rev": "f68c42c011c5fd48bb8be79071f729c131f35304", "type": "github" }, "original": { @@ -422,11 +422,11 @@ ] }, "locked": { - "lastModified": 1768764703, - "narHash": "sha256-5ulSDyOG1U+1sJhkJHYsUOWEsmtLl97O0NTVMvgIVyc=", + "lastModified": 1770184146, + "narHash": "sha256-DsqnN6LvXmohTRaal7tVZO/AKBuZ02kPBiZKSU4qa/k=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "0fc4e7ac670a0ed874abacf73c4b072a6a58064b", + "rev": "0d7874ef7e3ba02d58bebb871e6e29da36fa1b37", "type": "github" }, "original": { @@ -500,11 +500,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1769527094, - "narHash": "sha256-xV20Alb7ZGN7qujnsi5lG1NckSUmpIb05H2Xar73TDc=", + "lastModified": 1770169770, + "narHash": "sha256-awR8qIwJxJJiOmcEGgP2KUqYmHG4v/z8XpL9z8FnT1A=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "afce96367b2e37fc29afb5543573cd49db3357b7", + "rev": "aa290c9891fa4ebe88f8889e59633d20cc06a5f2", "type": "github" }, "original": { @@ -516,11 +516,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1765674936, - "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", + "lastModified": 1769909678, + "narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", + "rev": "72716169fe93074c333e8d0173151350670b824c", "type": "github" }, "original": { @@ -537,11 +537,11 @@ ] }, "locked": { - "lastModified": 1769745905, - "narHash": "sha256-huISbiqnYDtwjXDVV8NmMyMNAHVNPosiKxCQ9chvWbs=", + "lastModified": 1770392205, + "narHash": "sha256-twpTMfpAknPqMxjwUqyv6fvaw+xLs3MhMKLxqXbO3zw=", "owner": "nix-community", "repo": "NUR", - "rev": "9c0ebcd2bebba1e9fa3335aa280361495d86a715", + "rev": "dbad547d939147cbc5313215a4722d276316c225", "type": "github" }, "original": { @@ -611,11 +611,11 @@ ] }, "locked": { - "lastModified": 1769691507, - "narHash": "sha256-8aAYwyVzSSwIhP2glDhw/G0i5+wOrren3v6WmxkVonM=", + "lastModified": 1770228511, + "narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b", + "rev": "337a4fe074be1042a35086f15481d763b8ddc0e7", "type": "github" }, "original": { diff --git a/home-manager/default.nix b/home-manager/default.nix index 7425553a3..c83866c39 100644 --- a/home-manager/default.nix +++ b/home-manager/default.nix @@ -7,7 +7,7 @@ ... }: let - hmConfig = import ../config; + hmConfig = import ../config { inherit inputs; }; packages = import ./packages { inherit pkgs inputs; }; misc = import ./misc; modules = import ./modules; diff --git a/home-manager/packages/default.nix b/home-manager/packages/default.nix index 36c8b15a1..e73f226a6 100644 --- a/home-manager/packages/default.nix +++ b/home-manager/packages/default.nix @@ -114,16 +114,37 @@ with pkgs; powertop tailscale trashy + xclip ] ++ lib.optionals (stdenv.isLinux && isDesktop) [ _1password-gui + brightnessctl chromium clickup + cliphist + emote ffmpeg ghostty github-desktop google-chrome + grim + hypridle + hyprlock + libnotify + networkmanagerapplet + pavucontrol + playerctl signal-desktop slack + slurp + swappy + swaynotificationcenter vlc + vscode + waybar + wl-clipboard + wlogout + wob + wofi + wofi-emoji ] diff --git a/home-manager/programs/fish/functions/fish_user_key_bindings.fish b/home-manager/programs/fish/functions/fish_user_key_bindings.fish index 453c73d0d..9e59c0f29 100644 --- a/home-manager/programs/fish/functions/fish_user_key_bindings.fish +++ b/home-manager/programs/fish/functions/fish_user_key_bindings.fish @@ -5,4 +5,8 @@ function fish_user_key_bindings # Exit vim insert mode with jj (like in Vim) bind -M insert jj 'set fish_bind_mode default; commandline -f repaint-mode' + + # Ctrl+V = paste from clipboard (macOS-style via keyd Framework key) + bind \cv fish_clipboard_paste + bind -M insert \cv fish_clipboard_paste end diff --git a/named-hosts/galactica/keys/gpg.age b/named-hosts/galactica/keys/gpg.age index b8b81d91b..cb2fe18b7 100644 Binary files a/named-hosts/galactica/keys/gpg.age and b/named-hosts/galactica/keys/gpg.age differ diff --git a/named-hosts/galactica/keys/id_ed25519.age b/named-hosts/galactica/keys/id_ed25519.age index 2a2570a02..f66c54b89 100644 Binary files a/named-hosts/galactica/keys/id_ed25519.age and b/named-hosts/galactica/keys/id_ed25519.age differ diff --git a/named-hosts/galactica/secrets.nix b/named-hosts/galactica/secrets.nix index 790302c11..1c7cb4416 100644 --- a/named-hosts/galactica/secrets.nix +++ b/named-hosts/galactica/secrets.nix @@ -3,13 +3,22 @@ let galactica = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com"; # Kyber's SSH public key kyber = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO0IZtP3KSzY6GVSZ+R+VQYYfu3sEOVaQGDblQxAtwNM ubuntu@kyber"; + # Matic's SSH public key + matic = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQqWYSDaaoazVNOrAimCpUxNgaLe9Von237zIoox3E5 skakinoki@matic"; # All machines that can decrypt shared secrets allMachines = [ galactica kyber + matic ]; in { + # SSH key for GitHub authentication (shared with all machines) + "keys/id_ed25519.age" = { + file = ./keys/id_ed25519.age; + publicKeys = allMachines; + }; + # GPG key (shared with all machines for commit signing) "keys/gpg.age" = { file = ./keys/gpg.age; diff --git a/named-hosts/matic/default.nix b/named-hosts/matic/default.nix index f45df9f6b..fb9bba17f 100644 --- a/named-hosts/matic/default.nix +++ b/named-hosts/matic/default.nix @@ -33,6 +33,9 @@ inputs.nixpkgs.lib.nixosSystem { # Kolide launcher ./kolide.nix + # Keyd configuration + ../../config/keyd + # Base system configuration ( { config, lib, ... }: @@ -89,10 +92,48 @@ inputs.nixpkgs.lib.nixosSystem { # Power management services.power-profiles-daemon.enable = true; - # Desktop environment (GNOME) + # Auto timezone (via geolocation) + services.geoclue2.enable = true; + services.localtimed.enable = true; + + # Audio (PipeWire) + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + wireplumber.enable = true; + }; + services.pulseaudio.enable = false; + security.rtkit.enable = true; + + # Bluetooth + hardware.bluetooth.enable = true; + hardware.bluetooth.powerOnBoot = true; + services.blueman.enable = true; + + # Desktop environment (Hyprland) services.xserver.enable = true; services.displayManager.gdm.enable = true; - services.desktopManager.gnome.enable = true; + services.displayManager.defaultSession = "hyprland"; + services.desktopManager.gnome.enable = false; + + programs.hyprland = { + enable = true; + package = pkgs.hyprland; + portalPackage = pkgs.xdg-desktop-portal-hyprland; + xwayland.enable = true; + withUWSM = true; + }; + + programs.dconf.enable = true; + + xdg.portal = { + enable = true; + extraPortals = with pkgs; [ + xdg-desktop-portal-gtk + ]; + }; # WiFi MT7925e fix (disable ASPM) boot.extraModprobeConfig = '' @@ -128,9 +169,6 @@ inputs.nixpkgs.lib.nixosSystem { "nix-command" "flakes" ]; - substituters = [ - "https://cache.nixos.org" - ]; trusted-users = [ "root" username @@ -193,18 +231,121 @@ inputs.nixpkgs.lib.nixosSystem { }; home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; - home-manager.users.${username} = import ../../home-manager { - inherit username; - # Override host detection for matic (isDesktop = true) - inputs = inputs // { - host = (import ../../lib/host.nix) // { - isDesktop = true; + home-manager.users.${username} = + { config, lib, ... }: + { + imports = [ + (import ../../home-manager { + inherit username; + # Override host detection for matic (isDesktop = true) + inputs = inputs // { + host = (import ../../lib/host.nix) // { + isDesktop = true; + }; + }; + lib = inputs.nixpkgs.lib; + pkgs = pkgs; + config = { }; + }) + ]; + + # Agenix configuration for GitHub SSH key + age.identityPaths = [ "/home/${username}/.ssh/id_ed25519" ]; + age.secrets = builtins.mapAttrs ( + name: value: + { + file = value.file; + } + // ( + if name == "keys/id_github.age" then + { + # Deploy GitHub SSH key to ~/.ssh/ with correct permissions + path = "/home/${username}/.ssh/id_ed25519_github"; + mode = "0600"; + } + else + { } + ) + ) (import ./secrets.nix); + + # Ensure SSH directory exists before agenix tries to deploy secrets + home.activation.ensureSshDirectory = config.lib.dag.entryBefore [ "writeBoundary" ] '' + $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.home.homeDirectory}/.ssh + $DRY_RUN_CMD chmod $VERBOSE_ARG 700 ${config.home.homeDirectory}/.ssh + ''; + + # Ensure agenix config directory exists + home.activation.ensureAgenixDirectory = config.lib.dag.entryBefore [ "writeBoundary" ] '' + $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.home.homeDirectory}/.config/agenix + $DRY_RUN_CMD chmod $VERBOSE_ARG 700 ${config.home.homeDirectory}/.config/agenix + ''; + + # Manually deploy agenix secrets during activation + # This ensures secrets are deployed even if the agenix activation hook doesn't run properly + home.activation.deployAgenixSecrets = config.lib.dag.entryAfter [ "writeBoundary" ] '' + # Decrypt and deploy GitHub SSH key if it doesn't exist + if [[ ! -f "${config.home.homeDirectory}/.ssh/id_ed25519_github" ]]; then + echo "Deploying GitHub SSH key from agenix..." + SECRET_FILE="${builtins.toString ../galactica/keys/id_ed25519.age}" + if [[ -f "$SECRET_FILE" ]]; then + $DRY_RUN_CMD ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 "$SECRET_FILE" -o ${config.home.homeDirectory}/.ssh/id_ed25519_github + $DRY_RUN_CMD chmod $VERBOSE_ARG 0600 ${config.home.homeDirectory}/.ssh/id_ed25519_github + echo "โœ… GitHub SSH key deployed successfully" + else + echo "โš ๏ธ Warning: Secret file not found at $SECRET_FILE" + fi + fi + ''; + + # Import GPG key from agenix (all systems with dotfiles) + # Fails silently if SSH key isn't authorized to decrypt + home.activation.importGpgKey = config.lib.dag.entryAfter [ "linkGeneration" ] '' + $VERBOSE_ECHO "๐Ÿ”‘ Starting GPG key import process..." + GPG_SECRET_FILE="${config.home.homeDirectory}/dotfiles/named-hosts/galactica/keys/gpg.age" + GPG_TEMP_FILE="${config.home.homeDirectory}/.config/agenix/gpg.key" + + # Create agenix directory if it doesn't exist + mkdir -p "${config.home.homeDirectory}/.config/agenix" + + if [[ -f "$GPG_SECRET_FILE" ]]; then + # Check if key is already imported + if ! ${pkgs.gnupg}/bin/gpg --list-secret-keys 2>/dev/null | grep -q "C2E97FCFF482925D"; then + echo "Importing GPG key from agenix..." + # Try to decrypt - will fail silently if SSH key isn't authorized + if ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 -o "$GPG_TEMP_FILE" "$GPG_SECRET_FILE" 2>/dev/null; then + ${pkgs.gnupg}/bin/gpg --batch --import "$GPG_TEMP_FILE" 2>/dev/null + rm -f "$GPG_TEMP_FILE" + echo "โœ… GPG key imported successfully" + fi + else + $VERBOSE_ECHO "โ„น๏ธ GPG key already imported" + fi + fi + ''; + + # GPG configuration for commit signing + programs.gpg = { + enable = true; + settings = { + default-key = "shunkakinoki@gmail.com"; + }; }; + + # GPG agent configuration + services.gpg-agent = { + enable = true; + enableSshSupport = false; + pinentry.package = pkgs.pinentry-tty; + defaultCacheTtl = 94608000; # 3 years + maxCacheTtl = 94608000; # 3 years + }; + + # GPG_TTY is set in fish shell init instead of sessionVariables + # because it needs to be evaluated dynamically per shell session + programs.fish.interactiveShellInit = lib.mkAfter '' + set -gx GPG_TTY (tty) + ''; }; - lib = inputs.nixpkgs.lib; - pkgs = pkgs; - config = { }; - }; } ] ++ (if falconDebExists then [ ./falcon.nix ] else [ ]); # CrowdStrike Falcon (only if .deb exists) diff --git a/named-hosts/matic/secrets.nix b/named-hosts/matic/secrets.nix new file mode 100644 index 000000000..cb41aa23e --- /dev/null +++ b/named-hosts/matic/secrets.nix @@ -0,0 +1,20 @@ +# Matic secrets - synced from galactica via agenix +let + galactica = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com"; + matic = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQqWYSDaaoazVNOrAimCpUxNgaLe9Von237zIoox3E5 skakinoki@matic"; + allMachines = [ + galactica + matic + ]; +in +{ + "keys/id_github.age" = { + file = ../galactica/keys/id_ed25519.age; + publicKeys = allMachines; + }; + + "keys/gpg.age" = { + file = ../galactica/keys/gpg.age; + publicKeys = allMachines; + }; +}