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