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