From 20fbee59da96005edf5d3a0b7447e43fcb851714 Mon Sep 17 00:00:00 2001 From: Scott Scoble Date: Tue, 12 May 2026 21:50:52 -0700 Subject: [PATCH] Refactor: home-manager cross-platform + WGU software - Split home config: sscoble.nix (shared macOS+NixOS) + desktop.nix (Hyprland) - Added homeConfigurations.sscoble for work Mac (aarch64-darwin) - Added home/wgu.nix: Zotero, Brave, Obsidian, VSCode, Typst, Bitwarden - Mac: home-manager switch --flake .#sscoble - NixOS: nixos-rebuild switch --flake .#laptop --- flake.nix | 67 +++-- home/desktop.nix | 375 ++++++++++++++++++++++++ home/sscoble.nix | 732 ++++++----------------------------------------- home/wgu.nix | 36 +++ 4 files changed, 544 insertions(+), 666 deletions(-) create mode 100644 home/desktop.nix create mode 100644 home/wgu.nix diff --git a/flake.nix b/flake.nix index debdd1f..c12b0e9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Scobleclaw laptop — NixOS configuration reproducing MacBook software capabilities on x86_64"; + description = "Scobleclaw — NixOS + home-manager configuration for personal laptop (x86_64) and work Mac (aarch64)"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -14,27 +14,26 @@ }; outputs = { self, nixpkgs, home-manager, pi-mono, ... }@inputs: let - # System we're targeting (x86_64 NixOS laptop) - system = "x86_64-linux"; + linuxSystem = "x86_64-linux"; + darwinSystem = "aarch64-darwin"; - pkgs = import nixpkgs { - inherit system; + # Linux pkgs with overlay (pi, qmd) + linuxPkgs = import nixpkgs { + system = linuxSystem; overlays = [ self.overlays.default ]; }; in { - # Overlay — add pi and qmd to nixpkgs + # === Overlay: pi + qmd (Linux only) === overlays.default = final: prev: { - # pi coding agent — built from Forgejo mirror of pi-mono pi-coding-agent = final.buildNpmPackage { pname = "pi-coding-agent"; version = "0.73.0"; src = pi-mono; - npmDepsHash = ""; # Set after first build: nix-build outputs the correct hash + npmDepsHash = ""; # Fill after first build npmWorkspace = "packages/coding-agent"; npmFlags = [ "--legacy-peer-deps" ]; dontNpmBuild = true; preConfigure = '' - # Build dependencies first (monorepo workspace order) cd packages/ai && npm run build && cd ../.. cd packages/agent && npm run build && cd ../.. cd packages/tui && npm run build && cd ../.. @@ -55,38 +54,51 @@ ''; }; - # qmd — vault search tool, wraps npm package via bun qmd = final.writeShellScriptBin "qmd" '' exec ${final.bun}/bin/bunx --bun @tobilu/qmd "$@" ''; - - # Pi config directory structure (without secrets) - pi-config-dir = final.runCommand "pi-config-dir" {} '' - mkdir -p $out - # Placeholder — real config should be restored from backup - echo "# Pi agent configuration" > $out/README.md - echo "# Defaults" > $out/settings.json - ''; }; - # NixOS configuration for the laptop + # === NixOS: full system + desktop === nixosConfigurations.laptop = nixpkgs.lib.nixosSystem { - inherit system; + system = linuxSystem; modules = [ home-manager.nixosModules.home-manager ./modules/laptop.nix ./modules/hyprland.nix { - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.users.sscoble = import ./home/sscoble.nix; + home-manager = { + useGlobalPkgs = true; + useUserPackages = true; + users.sscoble = { + imports = [ + ./home/sscoble.nix + ./home/desktop.nix + ./home/wgu.nix + ]; + }; + }; } ]; }; - # Standalone dev shell for trying things out - devShells.${system}.default = pkgs.mkShell { - buildInputs = with pkgs; [ + # === macOS: home-manager standalone (no desktop, no Hyprland) === + homeConfigurations.sscoble = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.${darwinSystem}; + modules = [ + ./home/sscoble.nix + ./home/wgu.nix + { + # macOS-specific overrides + home.homeDirectory = "/Users/sscoble"; + # pi/qmd come from homebrew, not nixpkgs + } + ]; + }; + + # === Dev shell (Linux) === + devShells.${linuxSystem}.default = linuxPkgs.mkShell { + buildInputs = with linuxPkgs; [ pi-coding-agent qmd nodejs @@ -95,12 +107,11 @@ zsh ]; shellHook = '' - echo "🦞 Scobleclaw laptop dev shell" + echo "🦞 Scobleclaw dev shell" echo " pi: $(which pi)" echo " qmd: $(which qmd)" echo " node: $(node --version)" echo " bun: $(bun --version)" - echo " git: $(git --version)" ''; }; }; diff --git a/home/desktop.nix b/home/desktop.nix new file mode 100644 index 0000000..7acd4c9 --- /dev/null +++ b/home/desktop.nix @@ -0,0 +1,375 @@ +{ config, pkgs, ... }: + +{ + # === Linux desktop packages === + home.packages = with pkgs; [ + # Terminal + ghostty + zellij + # Status bar + waybar + # App launcher + rofi-wayland + # Notifications + dunst + libnotify + # Wallpaper + swww + # Lock + idle + hyprlock + hypridle + # Screenshots + grim + slurp + swappy + # Clipboard + wl-clipboard + cliphist + # Brightness / audio + brightnessctl + pavucontrol + # File manager + thunar + # Polkit agent + polkit_gnome + # Theming + bibata-cursors + papirus-icon-theme + adw-gtk3 + # Network / containers / system + networkmanagerapplet + firefox + tailscale + docker + dive + nmap + tcpdump + speedtest-cli + ]; + + # === GTK / QT theming === + gtk = { + enable = true; + theme.name = "adw-gtk3-dark"; + iconTheme.name = "Papirus-Dark"; + cursorTheme = { + name = "Bibata-Modern-Classic"; + size = 24; + }; + }; + qt = { + enable = true; + platformTheme.name = "gtk"; + style.name = "adwaita-dark"; + }; + + # === Hyprland WM config === + home.file.".config/hypr/hyprland.conf".text = '' + monitor=,preferred,auto,1 + + # Environment + env = XCURSOR_SIZE,24 + env = XCURSOR_THEME,Bibata-Modern-Classic + env = GTK_THEME,adw-gtk3-dark + env = QT_QPA_PLATFORMTHEME,gtk2 + + # Autostart + exec-once = waybar + exec-once = dunst + exec-once = swww-daemon + exec-once = swww img ~/wallpapers/default.jpg + exec-once = wl-paste --type text --watch cliphist store + exec-once = wl-paste --type image --watch cliphist store + exec-once = hypridle + exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 + + # Input + input { + kb_layout = us + kb_options = caps:escape + follow_mouse = 1 + touchpad { natural_scroll = true; tap-to-click = true; } + sensitivity = 0 + } + + # Visual + general { + gaps_in = 6; gaps_out = 8; border_size = 2 + col.active_border = rgba(8ebdffee) rgba(8ebdffff) rgba(8ebdffee) rgba(8ebdffff) 45deg + col.inactive_border = rgba(333333aa) + layout = dwindle; allow_tearing = false + } + decoration { + rounding = 8 + blur { enabled = true; size = 5; passes = 2; new_optimizations = true; } + drop_shadow = true; shadow_range = 12; shadow_render_power = 2 + col.shadow = rgba(1a1a1aee) + } + animations { + enabled = true + bezier = easeOutQuint,0.23,1,0.32,1 + bezier = easeInOutCubic,0.65,0.05,0.36,1 + animation = windows,1,7,easeOutQuint,slide + animation = windowsOut,1,7,easeInOutCubic,slide + animation = fade,1,7,easeOutQuint + animation = workspaces,1,4,easeOutQuint,fade + } + + # Workspaces + workspace = 1, name:shell, persistent:true + workspace = 2, name:code, persistent:true + workspace = 3, name:web, persistent:true + workspace = 4, name:chat, persistent:true + workspace = 5, name:vault, persistent:true + workspace = 6, name:media + workspace = 7, name:7 + workspace = 8, name:8 + workspace = 9, name:9 + workspace = 10, name:sys + + # Keybinds + $mainMod = SUPER + + # Launch + bind = $mainMod, Return, exec, ghostty -e zellij + bind = $mainMod SHIFT, Return, exec, ghostty + bind = $mainMod, B, exec, firefox + bind = $mainMod, D, exec, rofi -show drun -show-icons + bind = $mainMod, Space, exec, rofi -show drun -show-icons + bind = $mainMod, V, exec, cliphist list | rofi -dmenu | cliphist decode | wl-copy + bind = $mainMod, N, exec, thunar + bind = $mainMod, L, exec, hyprlock + + # Window control + bind = $mainMod, Q, killactive, + bind = $mainMod, F, fullscreen + bind = $mainMod, G, togglefloating, + bind = $mainMod, P, pseudo, + bind = $mainMod, T, pin, + + # Focus (Vim-style) + bind = $mainMod, H, movefocus, l + bind = $mainMod, J, movefocus, d + bind = $mainMod, K, movefocus, u + bind = $mainMod, L, movefocus, r + + # Move windows + bind = $mainMod SHIFT, H, movewindow, l + bind = $mainMod SHIFT, J, movewindow, d + bind = $mainMod SHIFT, K, movewindow, u + bind = $mainMod SHIFT, L, movewindow, r + + # Resize + bind = $mainMod CTRL, H, resizeactive, -40 0 + bind = $mainMod CTRL, J, resizeactive, 0 40 + bind = $mainMod CTRL, K, resizeactive, 0 -40 + bind = $mainMod CTRL, L, resizeactive, 40 0 + + # Workspace switching + bind = $mainMod, 1, workspace, name:shell + bind = $mainMod, 2, workspace, name:code + bind = $mainMod, 3, workspace, name:web + bind = $mainMod, 4, workspace, name:chat + bind = $mainMod, 5, workspace, name:vault + bind = $mainMod, 6, workspace, name:media + bind = $mainMod, 7, workspace, name:7 + bind = $mainMod, 8, workspace, name:8 + bind = $mainMod, 9, workspace, name:9 + bind = $mainMod, 0, workspace, name:sys + + # Move to workspace + bind = $mainMod SHIFT, 1, movetoworkspace, name:shell + bind = $mainMod SHIFT, 2, movetoworkspace, name:code + bind = $mainMod SHIFT, 3, movetoworkspace, name:web + bind = $mainMod SHIFT, 4, movetoworkspace, name:chat + bind = $mainMod SHIFT, 5, movetoworkspace, name:vault + bind = $mainMod SHIFT, 6, movetoworkspace, name:media + bind = $mainMod SHIFT, 7, movetoworkspace, name:7 + bind = $mainMod SHIFT, 8, movetoworkspace, name:8 + bind = $mainMod SHIFT, 9, movetoworkspace, name:9 + bind = $mainMod SHIFT, 0, movetoworkspace, name:sys + + # Scroll workspace + bind = $mainMod, mouse_down, workspace, e+1 + bind = $mainMod, mouse_up, workspace, e-1 + + # Screenshot + bind = $mainMod, S, exec, grim -g "$(slurp)" - | swappy -f - + bind = $mainMod SHIFT, S, exec, grim - | swappy -f - + + # Media keys + bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ + bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- + bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle + bindel = , XF86MonBrightnessUp, exec, brightnessctl set 5%+ + bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%- + + # Session + bind = $mainMod SHIFT, Q, exec, hyprctl dispatch exit + bind = $mainMod SHIFT, R, exec, hyprctl reload + + # Window rules + windowrulev2 = float, class:(pavucontrol) + windowrulev2 = float, class:(firefox), title:(Picture-in-Picture) + windowrulev2 = float, title:(Authentication Required) + windowrulev2 = size 75% 75%,class:(pavucontrol) + + # Auto-send to workspaces + windowrulev2 = workspace name:code, class:(code-oss|Code|jetbrains-.*|nvim) + windowrulev2 = workspace name:web, class:(firefox|chromium-browser|google-chrome) + windowrulev2 = workspace name:chat, class:(discord|Slack|TelegramDesktop) + windowrulev2 = workspace name:shell, class:(ghostty|Zellij|Alacritty) + ''; + + # === Ghostty config === + home.file.".config/ghostty/config".text = '' + font-family = JetBrainsMono Nerd Font + font-size = 13 + theme = catppuccin-mocha + background-opacity = 0.9 + window-padding-x = 8 + window-padding-y = 4 + cursor-style = bar + confirm-close-surface = false + shell-integration = zsh + gtk-single-instance = true + ''; + + # === Zellij config === + home.file.".config/zellij/config.kdl".text = '' + theme "catppuccin-mocha" + default_shell "zsh" + pane_frames false + scrollback_editor "nvim" + + keybinds clear-defaults=true { + normal { bind "Ctrl g" { SwitchToMode "locked"; } } + locked { + bind "Ctrl g" { SwitchToMode "normal"; } + bind "Ctrl q" { CloseTab; } + bind "Ctrl p" { NewPane "down"; } + bind "Ctrl n" { NewPane "right"; } + bind "Ctrl t" { NewTab; } + bind "Ctrl x" { CloseFocus; } + bind "Ctrl f" { ToggleFloatingPanes; } + bind "Ctrl z" { ToggleFocusFullscreen; } + bind "Ctrl d" { Detach; } + bind "Ctrl h" { MoveFocusOrTab "left"; } + bind "Ctrl j" { MoveFocusOrTab "down"; } + bind "Ctrl k" { MoveFocusOrTab "up"; } + bind "Ctrl l" { MoveFocusOrTab "right"; } + bind "Ctrl r" { SwitchToMode "renametab"; } + } + renametab { + bind "Ctrl c" { SwitchToMode "locked"; } + bind "Esc" { SwitchToMode "locked"; } + } + } + ''; + + # === Hyprlock === + home.file.".config/hypr/hyprlock.conf".text = '' + general { disable_loading_bar = false; hide_cursor = true; grace = 0; no_fade_in = false; } + background { path = screenshot; blur_passes = 3; blur_size = 10; color = rgba(0,0,0,0.6); } + input-field { + size = 250, 50; position = 0, -200; monitor = + dots_center = true; fade_on_empty = false + placeholder_text = unlock + outer_color = rgba(8ebdff,0.8); inner_color = rgba(30,30,46,0.8) + font_color = rgba(255,255,255,0.9) + } + label { + text = cmd[update:60] date +"%A, %B %d %H:%M" + position = 0, 200; monitor = + font_size = 32; font_family = JetBrains Mono Nerd Font + color = rgba(255,255,255,0.9) + } + ''; + + # === Hypridle === + home.file.".config/hypr/hypridle.conf".text = '' + general { lock_cmd = hyprlock; before_sleep_cmd = hyprlock; } + listener { timeout = 300; on-timeout = hyprlock; } + listener { timeout = 600; on-timeout = hyprctl dispatch dpms off; on-resume = hyprctl dispatch dpms on; } + ''; + + # === Waybar config === + home.file.".config/waybar/config.jsonc".text = builtins.toJSON { + layer = "top"; position = "top"; height = 32; spacing = 4; margin = "6 8 0 8"; + modules-left = [ "hyprland/workspaces" ]; + modules-center = [ "clock" ]; + modules-right = [ "tray" "pulseaudio" "backlight" "battery" "network" "cpu" "memory" ]; + + "hyprland/workspaces" = { + format = "{icon}"; + format-icons = { + shell = "󰆍"; code = "󰨞"; web = "󰖟"; chat = "󰭻"; vault = "󰠮"; + media = "󰎆"; "7" = "󰎇"; "8" = "󰎈"; "9" = "󰎉"; sys = "󰒓"; default = "󰊌"; + }; + persistent_workspaces = { "1" = ["shell"]; "2" = ["code"]; "3" = ["web"]; "4" = ["chat"]; "5" = ["vault"]; }; + }; + clock = { format = "{:%a %b %d %H:%M}"; tooltip-format = "{:%Y %B}\n{calendar}"; }; + cpu = { format = " {usage}%"; }; + memory = { format = "󰍛 {}%"; }; + backlight = { format = "{icon} {percent}%"; format-icons = [ "󰃝" "󰃞" "󰃟" "󰃠" ]; }; + battery = { + format = "{icon} {capacity}%"; + format-icons = [ "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; + format-charging = "󰂄 {capacity}%"; + }; + network = { + format-wifi = "󰖩 {signalStrength}%"; format-ethernet = "󰈀"; format-disconnected = "⚠"; + tooltip-format = "{ifname} via {gwaddr}"; + }; + pulseaudio = { + format = "{icon} {volume}%"; format-bluetooth = "{icon}󰂯 {volume}%"; format-muted = "󰝟"; + format-icons.default = [ "󰕿" "󰖀" "󰕾" ]; on-click = "pavucontrol"; + }; + tray.spacing = 10; + }; + + # === Waybar style === + home.file.".config/waybar/style.css".text = '' + * { font-family: "JetBrainsMono Nerd Font", "Font Awesome 6 Free"; font-size: 13px; } + window#waybar { background: rgba(30,30,46,0.85); border-radius: 8px; color: #cdd6f4; } + #workspaces button { padding: 0 6px; color: #6c7086; background: transparent; border-radius: 4px; } + #workspaces button.active { color: #cdd6f4; background: #45475a; } + #workspaces button:hover { color: #cdd6f4; background: #313244; } + #clock, #cpu, #memory, #backlight, #battery, #network, #pulseaudio, #tray { padding: 0 10px; color: #cdd6f4; } + #cpu.warning, #memory.warning, #battery.warning { color: #f9e2af; } + #cpu.critical, #memory.critical, #battery.critical { color: #f38ba8; } + tooltip { background: rgba(30,30,46,0.95); border: 1px solid #45475a; border-radius: 6px; } + ''; + + # === Rofi launcher === + home.file.".config/rofi/config.rasi".text = '' + configuration { + modi: "drun,run,window"; show-icons: true; icon-theme: "Papirus-Dark"; + drun-display-format: "{name}"; font: "JetBrainsMono Nerd Font 13"; matching: "normal"; + } + window { width: 600px; border-radius: 10px; background-color: rgba(30,30,46,0.95); border: 2px solid #89b4fa; } + mainbox { padding: 8px; } + inputbar { padding: 4px 8px; background-color: rgba(49,50,68,0.8); border-radius: 8px; margin-bottom: 4px; } + entry { background: transparent; text-color: #cdd6f4; caret-color: #cdd6f4; } + listview { background: transparent; lines: 8; } + element { padding: 4px 8px; border-radius: 6px; text-color: #cdd6f4; } + element selected { background-color: rgba(137,180,250,0.3); text-color: #ffffff; } + element-text { vertical-align: 0.5; } + ''; + + # === Dunst notifications === + home.file.".config/dunst/dunstrc".text = '' + [global] + monitor = 0; follow = mouse; width = 350; height = 150; offset = 10x50; origin = top-right + notification_limit = 5; indicate_hidden = yes; shrink = no; transparency = 0 + separator_height = 2; padding = 12; horizontal_padding = 12; frame_width = 2 + frame_color = "#89b4fa"; separator_color = frame; sort = yes + font = JetBrainsMono Nerd Font 11; corner_radius = 8 + background = "#1e1e2e"; foreground = "#cdd6f4"; timeout = 5 + idle_threshold = 120; icon_position = left; max_icon_size = 48; markup = full + format = "%s\n%b" + [urgency_low] background = "#1e1e2e"; foreground = "#cdd6f4"; timeout = 3 + [urgency_normal] background = "#1e1e2e"; foreground = "#cdd6f4"; timeout = 5 + [urgency_critical] background = "#f38ba8"; foreground = "#1e1e2e"; timeout = 0 + ''; +} diff --git a/home/sscoble.nix b/home/sscoble.nix index 15c28f1..6be3203 100644 --- a/home/sscoble.nix +++ b/home/sscoble.nix @@ -2,80 +2,50 @@ { home.username = "sscoble"; - home.homeDirectory = "/home/sscoble"; home.stateVersion = "25.11"; # Don't let home-manager manage these manual.json.enable = false; manual.manpages.enable = false; - # === Home Packages === + # === Shared CLI packages (macOS + NixOS) === home.packages = with pkgs; [ - # Python for scripting - python3 - uv - - # Go - go - - # Container tools - docker - dive - - # Network tools - nmap - tcpdump - speedtest-cli - tailscale - - # Extra editors / viewers - bat # better cat - eza # better ls - delta # better git diff - tldr # simplified man pages - - # File sync + bat + eza + delta + fd + jq + ripgrep + tldr rsync rclone + neovim + python3 + uv + go + direnv + nix-direnv + tree + htop + tmux + unzip + curl + wget + ] ++ (if pkgs.stdenv.isLinux then (with pkgs; [ + # Linux-specific packages + pi-coding-agent + qmd + nodejs + bun + git + ]) else [ + # macOS: pi, qmd, node, bun, git come from homebrew + ]); - # === Hyprland desktop === - # Status bar - waybar - # App launcher - rofi-wayland - # Terminal - ghostty - # Terminal multiplexer (cmux equivalent for Linux) - zellij - # Notifications - dunst - libnotify # notify-send - # Wallpaper - swww - # Lock screen + idle - hyprlock - hypridle - # Screenshots - grim - slurp - swappy # annotation - # Clipboard - wl-clipboard - cliphist # clipboard history - # Brightness / audio control - brightnessctl - pavucontrol - # File manager - thunar - # Polkit agent (launched by systemd, but binary needed) - polkit_gnome - # Cursor theme - bibata-cursors - # Icons - papirus-icon-theme - # GTK theme - adw-gtk3 - ]; + # === Home directory (conditional) === + home.homeDirectory = if pkgs.stdenv.isDarwin + then "/Users/sscoble" + else "/home/sscoble"; # === Git Config === programs.git = { @@ -86,7 +56,7 @@ init.defaultBranch = "main"; pull.rebase = true; push.autoSetupRemote = true; - core.editor = "vim"; + core.editor = "nvim"; }; aliases = { lol = "log --graph --oneline --decorate --all"; @@ -97,578 +67,51 @@ }; }; - # === Pi Config Directory === - # Creates ~/.pi/agent/ with template files — user should restore real configs - home.file.".pi/agent/README.md" = { - text = '' - # Pi Agent Configuration - Restore these files from backup: - - SYSTEM.md (core agent directive) - - models.json (provider + model configuration) - - mcp.json (MCP server connections) - - settings.json (pi preferences) - - auth.json (API keys — keep secure!) - ''; - }; - - # === Neovim minimal config === - home.file.".config/nvim/init.lua" = { - text = '' - vim.opt.number = true - vim.opt.relativenumber = true - vim.opt.expandtab = true - vim.opt.shiftwidth = 2 - vim.opt.tabstop = 2 - vim.opt.smartindent = true - vim.opt.wrap = false - vim.opt.swapfile = false - vim.opt.backup = false - vim.opt.undodir = os.getenv("HOME") .. "/.vim/undodir" - vim.opt.undofile = true - vim.opt.hlsearch = false - vim.opt.incsearch = true - vim.opt.termguicolors = true - vim.opt.scrolloff = 8 - vim.opt.signcolumn = "yes" - vim.opt.isfname:append("@-@") - vim.opt.updatetime = 50 - vim.opt.colorcolumn = "100" - vim.g.mapleader = " " - ''; - }; - - # === Vault directory placeholder === - home.file."vault/.gitkeep" = { text = ""; }; - - # ============================================================================ - # Hyprland Desktop Configuration - # ============================================================================ - - # --- GTK / QT theming (applied to apps running under Hyprland) - gtk = { - enable = true; - theme.name = "adw-gtk3-dark"; - iconTheme.name = "Papirus-Dark"; - cursorTheme = { - name = "Bibata-Modern-Classic"; - size = 24; - }; - }; - qt = { - enable = true; - platformTheme.name = "gtk"; - style.name = "adwaita-dark"; - }; - - # --- Hyprland WM config --- - home.file.".config/hypr/hyprland.conf".text = '' - # ██╗ ██╗██╗ ██╗██████╗ ██████╗ ██╗ █████╗ ███╗ ██╗██████╗ - # ██║ ██║╚██╗ ██╔╝██╔══██╗██╔══██╗██║ ██╔══██╗████╗ ██║██╔══██╗ - # ███████║ ╚████╔╝ ██████╔╝██████╔╝██║ ███████║██╔██╗ ██║██║ ██║ - # ██╔══██║ ╚██╔╝ ██╔═══╝ ██╔══██╗██║ ██╔══██║██║╚██╗██║██║ ██║ - # ██║ ██║ ██║ ██║ ██║ ██║███████╗██║ ██║██║ ╚████║██████╔╝ - # ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ - - # ============ MONITORS ============ - monitor=,preferred,auto,1 - - # ============ ENVIRONMENT ============ - env = XCURSOR_SIZE,24 - env = XCURSOR_THEME,Bibata-Modern-Classic - env = GTK_THEME,adw-gtk3-dark - env = QT_QPA_PLATFORMTHEME,gtk2 - env = LIBVA_DRIVER_NAME,iHD # Intel hardware acceleration - - # ============ AUTOSTART ============ - exec-once = waybar - exec-once = dunst - exec-once = swww-daemon - exec-once = swww img ~/wallpapers/default.jpg # create this dir, add a wallpaper - exec-once = wl-paste --type text --watch cliphist store # clipboard history - exec-once = wl-paste --type image --watch cliphist store - exec-once = hypridle - exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 - - # ============ INPUT ============ - input { - kb_layout = us - kb_options = caps:escape - follow_mouse = 1 - touchpad { - natural_scroll = true - tap-to-click = true - } - sensitivity = 0 # -1.0 to 1.0 - } - - # ============ GENERAL ============ - general { - gaps_in = 6 - gaps_out = 8 - border_size = 2 - col.active_border = rgba(8ebdffee) rgba(8ebdffff) rgba(8ebdffee) rgba(8ebdffff) 45deg - col.inactive_border = rgba(333333aa) - layout = dwindle - allow_tearing = false - } - - # ============ DECORATION ============ - decoration { - rounding = 8 - blur { - enabled = true - size = 5 - passes = 2 - new_optimizations = true - } - drop_shadow = true - shadow_range = 12 - shadow_render_power = 2 - col.shadow = rgba(1a1a1aee) - } - - # ============ ANIMATIONS ============ - animations { - enabled = true - bezier = easeOutQuint,0.23,1,0.32,1 - bezier = easeInOutCubic,0.65,0.05,0.36,1 - animation = windows,1,7,easeOutQuint,slide - animation = windowsOut,1,7,easeInOutCubic,slide - animation = fade,1,7,easeOutQuint - animation = workspaces,1,4,easeOutQuint,fade - } - - # ============ WORKSPACES ============ - # Named workspaces — see waybar config for matching icons - workspace = 1, name:shell, persistent:true - workspace = 2, name:code, persistent:true - workspace = 3, name:web, persistent:true - workspace = 4, name:chat, persistent:true - workspace = 5, name:vault, persistent:true - workspace = 6, name:media - workspace = 7, name:7 - workspace = 8, name:8 - workspace = 9, name:9 - workspace = 10, name:sys - - # ============ KEYBINDS ============ - # Mod key: SUPER (Windows key) - $mainMod = SUPER - - # --- Launch --- - bind = $mainMod, Return, exec, ghostty -e zellij - bind = $mainMod SHIFT, Return, exec, ghostty - bind = $mainMod, B, exec, firefox - bind = $mainMod, D, exec, rofi -show drun -show-icons - bind = $mainMod, Space, exec, rofi -show drun -show-icons - bind = $mainMod, V, exec, cliphist list | rofi -dmenu | cliphist decode | wl-copy - bind = $mainMod, N, exec, thunar - bind = $mainMod, L, exec, hyprlock - - # --- Window control --- - bind = $mainMod, Q, killactive, - bind = $mainMod, F, fullscreen - bind = $mainMod, G, togglefloating, - bind = $mainMod, P, pseudo, # dwindle split - bind = $mainMod, T, pin, # keep on top - - # --- Focus --- - bind = $mainMod, H, movefocus, l - bind = $mainMod, J, movefocus, d - bind = $mainMod, K, movefocus, u - bind = $mainMod, L, movefocus, r - # Arrow keys alternative - bind = $mainMod, left, movefocus, l - bind = $mainMod, down, movefocus, d - bind = $mainMod, up, movefocus, u - bind = $mainMod, right, movefocus, r - - # --- Move windows --- - bind = $mainMod SHIFT, H, movewindow, l - bind = $mainMod SHIFT, J, movewindow, d - bind = $mainMod SHIFT, K, movewindow, u - bind = $mainMod SHIFT, L, movewindow, r - - # --- Resize --- - bind = $mainMod CTRL, H, resizeactive, -40 0 - bind = $mainMod CTRL, J, resizeactive, 0 40 - bind = $mainMod CTRL, K, resizeactive, 0 -40 - bind = $mainMod CTRL, L, resizeactive, 40 0 - - # --- Workspaces --- - bind = $mainMod, 1, workspace, name:shell - bind = $mainMod, 2, workspace, name:code - bind = $mainMod, 3, workspace, name:web - bind = $mainMod, 4, workspace, name:chat - bind = $mainMod, 5, workspace, name:vault - bind = $mainMod, 6, workspace, name:media - bind = $mainMod, 7, workspace, name:7 - bind = $mainMod, 8, workspace, name:8 - bind = $mainMod, 9, workspace, name:9 - bind = $mainMod, 0, workspace, name:sys - - # --- Move to workspace --- - bind = $mainMod SHIFT, 1, movetoworkspace, name:shell - bind = $mainMod SHIFT, 2, movetoworkspace, name:code - bind = $mainMod SHIFT, 3, movetoworkspace, name:web - bind = $mainMod SHIFT, 4, movetoworkspace, name:chat - bind = $mainMod SHIFT, 5, movetoworkspace, name:vault - bind = $mainMod SHIFT, 6, movetoworkspace, name:media - bind = $mainMod SHIFT, 7, movetoworkspace, name:7 - bind = $mainMod SHIFT, 8, movetoworkspace, name:8 - bind = $mainMod SHIFT, 9, movetoworkspace, name:9 - bind = $mainMod SHIFT, 0, movetoworkspace, name:sys - - # --- Scroll workspace (mouse) --- - bind = $mainMod, mouse_down, workspace, e+1 - bind = $mainMod, mouse_up, workspace, e-1 - - # --- Screenshot --- - bind = $mainMod, S, exec, grim -g "$(slurp)" - | swappy -f - - bind = $mainMod SHIFT, S, exec, grim - | swappy -f - - bind = , Print, exec, grim -g "$(slurp)" - | wl-copy - - # --- Media keys --- - bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ - bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- - bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle - bindel = , XF86MonBrightnessUp, exec, brightnessctl set 5%+ - bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%- - - # --- Session --- - bind = $mainMod SHIFT, Q, exec, hyprctl dispatch exit - bind = $mainMod SHIFT, R, exec, hyprctl reload - - # ============ WINDOW RULES ============ - windowrulev2 = float, class:(pavucontrol) - windowrulev2 = float, class:(org.gnome.Nautilus), title:(Properties) - windowrulev2 = float, class:(firefox), title:(Picture-in-Picture) - windowrulev2 = float, title:(Authentication Required) - windowrulev2 = size 75% 75%,class:(pavucontrol) - - # Send known apps to specific workspaces - windowrulev2 = workspace name:code, class:(code-oss|Code|jetbrains-.*|nvim) - windowrulev2 = workspace name:web, class:(firefox|chromium-browser|google-chrome) - windowrulev2 = workspace name:chat, class:(discord|Slack|TelegramDesktop) - windowrulev2 = workspace name:vault, class:(obsidian|qmd) - windowrulev2 = workspace name:shell, class:(ghostty|Zellij|Alacritty|kitty) + # === Neovim config === + home.file.".config/nvim/init.lua".text = '' + vim.opt.number = true + vim.opt.relativenumber = true + vim.opt.expandtab = true + vim.opt.shiftwidth = 2 + vim.opt.tabstop = 2 + vim.opt.smartindent = true + vim.opt.wrap = false + vim.opt.swapfile = false + vim.opt.backup = false + vim.opt.undodir = os.getenv("HOME") .. "/.vim/undodir" + vim.opt.undofile = true + vim.opt.hlsearch = false + vim.opt.incsearch = true + vim.opt.termguicolors = true + vim.opt.scrolloff = 8 + vim.opt.signcolumn = "yes" + vim.opt.isfname:append("@-@") + vim.opt.updatetime = 50 + vim.opt.colorcolumn = "100" + vim.g.mapleader = " " ''; - # --- Ghostty terminal config --- - home.file.".config/ghostty/config".text = '' - font-family = JetBrainsMono Nerd Font - font-size = 13 - theme = catppuccin-mocha - background-opacity = 0.9 - window-padding-x = 8 - window-padding-y = 4 - cursor-style = bar - confirm-close-surface = false - shell-integration = zsh - gtk-single-instance = true + # === Pi config directory === + home.file.".pi/agent/README.md".text = '' + # Pi Agent Configuration + Restore these files from backup: + - SYSTEM.md (core agent directive) + - models.json (provider + model configuration) + - mcp.json (MCP server connections) + - settings.json (pi preferences) + - auth.json (API keys — keep secure!) ''; - # --- Zellij config --- - home.file.".config/zellij/config.kdl".text = '' - theme "catppuccin-mocha" - default_shell "zsh" - pane_frames false - scrollback_editor "nvim" + # === Vault directory === + home.file."vault/.gitkeep".text = ""; - keybinds clear-defaults=true { - normal { - bind "Ctrl g" { SwitchToMode "locked"; } - } - locked { - bind "Ctrl g" { SwitchToMode "normal"; } - bind "Ctrl q" { CloseTab; } - bind "Ctrl p" { NewPane "down"; } - bind "Ctrl n" { NewPane "right"; } - bind "Ctrl h" { MoveFocusOrTab "left"; } - bind "Ctrl j" { MoveFocusOrTab "down"; } - bind "Ctrl k" { MoveFocusOrTab "up"; } - bind "Ctrl l" { MoveFocusOrTab "right"; } - bind "Ctrl t" { NewTab; } - bind "Ctrl x" { CloseFocus; } - bind "Ctrl f" { ToggleFloatingPanes; } - bind "Ctrl z" { ToggleFocusFullscreen; } - bind "Ctrl r" { SwitchToMode "renametab"; } - bind "Ctrl d" { Detach; } - } - renametab { - bind "Ctrl c" { SwitchToMode "locked"; } - bind "Esc" { SwitchToMode "locked"; } - } - } - ''; - - # --- Hyprlock config --- - home.file.".config/hypr/hyprlock.conf".text = '' - general { - disable_loading_bar = false - hide_cursor = true - grace = 0 - no_fade_in = false - } - background { - path = screenshot - blur_passes = 3 - blur_size = 10 - color = rgba(0,0,0,0.6) - } - input-field { - size = 250, 50 - position = 0, -200 - monitor = - dots_center = true - fade_on_empty = false - placeholder_text = unlock - outer_color = rgba(8ebdff,0.8) - inner_color = rgba(30,30,46,0.8) - font_color = rgba(255,255,255,0.9) - } - label { - text = cmd[update:60] date +"%A, %B %d %H:%M" - position = 0, 200 - monitor = - font_size = 32 - font_family = JetBrains Mono Nerd Font - color = rgba(255,255,255,0.9) - } - ''; - - # --- Hypridle config --- - home.file.".config/hypr/hypridle.conf".text = '' - general { - lock_cmd = hyprlock - before_sleep_cmd = hyprlock - } - listener { - timeout = 300 # 5 min - on-timeout = hyprlock - } - listener { - timeout = 600 # 10 min - on-timeout = hyprctl dispatch dpms off - on-resume = hyprctl dispatch dpms on - } - ''; - - # --- Waybar config --- - home.file.".config/waybar/config.jsonc".text = builtins.toJSON { - layer = "top"; - position = "top"; - height = 32; - spacing = 4; - margin = "6 8 0 8"; - - modules-left = [ - "hyprland/workspaces" - ]; - modules-center = [ - "clock" - ]; - modules-right = [ - "tray" - "pulseaudio" - "backlight" - "battery" - "network" - "cpu" - "memory" - ]; - - # Workspaces — icons match Hyprland names - "hyprland/workspaces" = { - format = "{icon}"; - format-icons = { - shell = "󰆍"; # 1: terminal - code = "󰨞"; # 2: development - web = "󰖟"; # 3: browser - chat = "󰭻"; # 4: communication - vault = "󰠮"; # 5: knowledge - media = "󰎆"; # 6: media - "7" = "󰎇"; # 7 - "8" = "󰎈"; # 8 - "9" = "󰎉"; # 9 - sys = "󰒓"; # 10: system - default = "󰊌"; - }; - persistent_workspaces = { - "1" = [ "shell" ]; - "2" = [ "code" ]; - "3" = [ "web" ]; - "4" = [ "chat" ]; - "5" = [ "vault" ]; - }; - }; - - clock = { - format = "{:%a %b %d %H:%M}"; - tooltip-format = "{:%Y %B}\n{calendar}"; - }; - - cpu = { - format = " {usage}%"; - }; - memory = { - format = "󰍛 {}%"; - }; - backlight = { - format = "{icon} {percent}%"; - format-icons = [ "󰃝" "󰃞" "󰃟" "󰃠" ]; - }; - battery = { - format = "{icon} {capacity}%"; - format-icons = [ "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; - format-charging = "󰂄 {capacity}%"; - }; - network = { - format-wifi = "󰖩 {signalStrength}%"; - format-ethernet = "󰈀"; - format-disconnected = "⚠"; - tooltip-format = "{ifname} via {gwaddr}"; - }; - pulseaudio = { - format = "{icon} {volume}%"; - format-bluetooth = "{icon}󰂯 {volume}%"; - format-muted = "󰝟"; - format-icons.default = [ "󰕿" "󰖀" "󰕾" ]; - on-click = "pavucontrol"; - }; - tray.spacing = 10; - }; - - # --- Waybar stylesheet --- - home.file.".config/waybar/style.css".text = '' - * { font-family: "JetBrainsMono Nerd Font", "Font Awesome 6 Free"; font-size: 13px; } - window#waybar { - background: rgba(30,30,46,0.85); - border-radius: 8px; - color: #cdd6f4; - } - #workspaces button { - padding: 0 6px; - color: #6c7086; - background: transparent; - border-radius: 4px; - } - #workspaces button.active { color: #cdd6f4; background: #45475a; } - #workspaces button:hover { color: #cdd6f4; background: #313244; } - #clock, #cpu, #memory, #backlight, #battery, #network, #pulseaudio, #tray { - padding: 0 10px; - color: #cdd6f4; - } - #cpu.warning { color: #f9e2af; } - #cpu.critical { color: #f38ba8; } - #memory.warning { color: #f9e2af; } - #memory.critical { color: #f38ba8; } - #battery.warning { color: #f9e2af; } - #battery.critical { color: #f38ba8; } - tooltip { - background: rgba(30,30,46,0.95); - border: 1px solid #45475a; - border-radius: 6px; - } - ''; - - # --- Rofi launcher --- - home.file.".config/rofi/config.rasi".text = '' - configuration { - modi: "drun,run,window"; - show-icons: true; - icon-theme: "Papirus-Dark"; - drun-display-format: "{name}"; - font: "JetBrainsMono Nerd Font 13"; - matching: "normal"; - disable-history: false; - } - window { - width: 600px; - border-radius: 10px; - background-color: rgba(30,30,46,0.95); - border: 2px solid #89b4fa; - } - mainbox { padding: 8px; } - inputbar { - padding: 4px 8px; - background-color: rgba(49,50,68,0.8); - border-radius: 8px; - margin-bottom: 4px; - } - entry { - background: transparent; - text-color: #cdd6f4; - caret-color: #cdd6f4; - } - listview { - background: transparent; - lines: 8; - } - element { - padding: 4px 8px; - border-radius: 6px; - text-color: #cdd6f4; - } - element selected { - background-color: rgba(137,180,250,0.3); - text-color: #ffffff; - } - element-text { vertical-align: 0.5; } - ''; - - # --- Dunst notifications --- - home.file.".config/dunst/dunstrc".text = '' - [global] - monitor = 0 - follow = mouse - width = 350 - height = 150 - offset = 10x50 - origin = top-right - notification_limit = 5 - indicate_hidden = yes - shrink = no - transparency = 0 - separator_height = 2 - padding = 12 - horizontal_padding = 12 - frame_width = 2 - frame_color = "#89b4fa" - separator_color = frame - sort = yes - font = JetBrainsMono Nerd Font 11 - corner_radius = 8 - background = "#1e1e2e" - foreground = "#cdd6f4" - timeout = 5 - idle_threshold = 120 - icon_position = left - max_icon_size = 48 - markup = full - format = "%s\n%b" - - [urgency_low] - background = "#1e1e2e" - foreground = "#cdd6f4" - timeout = 3 - [urgency_normal] - background = "#1e1e2e" - foreground = "#cdd6f4" - timeout = 5 - [urgency_critical] - background = "#f38ba8" - foreground = "#1e1e2e" - timeout = 0 - ''; - - # === Shell Direnv === + # === Direnv === programs.direnv = { enable = true; nix-direnv.enable = true; }; - # === SSH config (symlink to system config) === + # === SSH config === home.file.".ssh/config".text = '' Host n97-mini HostName 192.168.168.41 @@ -686,23 +129,36 @@ User git ''; - # === Zsh extra config === + # === Zsh === programs.zsh = { enable = true; + oh-my-zsh = { + enable = true; + plugins = [ "git" "node" "npm" "ssh-agent" "sudo" "history" ]; + theme = "robbyrussell"; + }; + autosuggestions.enable = true; + syntaxHighlighting.enable = true; + shellAliases = { + ll = "ls -la"; + la = "ls -A"; + l = "ls -CF"; + nix-clean = "sudo nix-collect-garbage -d && nix-collect-garbage -d"; + }; initExtra = '' - # Pi agent alias - alias pi="pi" + # Nix flake shortcuts (adapts to platform) + if command -v nixos-rebuild &>/dev/null; then + alias nr="sudo nixos-rebuild switch --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#laptop" + elif command -v home-manager &>/dev/null; then + alias nr="home-manager switch --flake git+https://os.scoble.me/forgejo/sscoble/laptop-config.git#sscoble" + fi - # QMD vault search - alias vq="qmd query" - - # Nix shortcuts - alias nr="sudo nixos-rebuild switch --flake ~/laptop-config#laptop" - alias ngc="nix-collect-garbage -d" + # Pi / QMD (work from homebrew or nixpkgs) + alias vq="qmd query" 2>/dev/null || alias vq="echo 'qmd not found — install with brew install qmd or bunx @tobilu/qmd'" ''; envExtra = '' - export EDITOR=vim - export VISUAL=vim + export EDITOR=nvim + export VISUAL=nvim ''; }; } diff --git a/home/wgu.nix b/home/wgu.nix new file mode 100644 index 0000000..13b88d4 --- /dev/null +++ b/home/wgu.nix @@ -0,0 +1,36 @@ +{ config, pkgs, ... }: + +{ + # === WGU / Academic software === + home.packages = with pkgs; [ + # Reference manager + zotero + + # Browsers + brave + firefox + + # Note-taking + obsidian + + # Code editor + vscode + + # Document preparation + typst + tinymist # Typst LSP for VS Code + pandoc + + # Password manager (LastPass has no Linux native client — use Bitwarden) + bitwarden-desktop + bitwarden-cli + ]; + + # === VS Code config (Typst LSP) === + home.file.".config/Code/User/settings.json".text = builtins.toJSON { + "typst-lsp.serverPath" = "${pkgs.tinymist}/bin/tinymist"; + "editor.fontFamily" = "JetBrainsMono Nerd Font"; + "editor.fontSize" = 14; + "workbench.colorTheme" = "Catppuccin Mocha"; + }; +}