From f367c4265125d65b965a846774a8949c07da0669 Mon Sep 17 00:00:00 2001 From: Scott Scoble Date: Thu, 21 May 2026 11:07:40 -0700 Subject: [PATCH] add obs, etc --- flake.nix | 1 + home/bin/lid-handler.sh | 56 +++++++ home/desktop.nix | 169 ++++++++++++++++++++- home/hyprland.conf | 17 ++- home/logos-icon.svg | 4 + home/macos.nix | 270 +++++++++++++++++++++++++++++++++ home/spotlight-dark.rasi | 150 ++++++++++++++++++ home/sscoble.nix | 24 +-- home/wgu.nix | 1 - modules/hyprland.nix | 52 +++++++ modules/laptop.nix | 11 +- modules/printing.nix | 2 +- modules/t490.nix | 53 ++++++- pi-overlay.nix | 3 + pkgs/beos-r5-icons/default.nix | 34 +++++ pkgs/haiku-cursor/default.nix | 28 ++++ 16 files changed, 844 insertions(+), 31 deletions(-) create mode 100755 home/bin/lid-handler.sh create mode 100644 home/logos-icon.svg create mode 100644 home/macos.nix create mode 100644 home/spotlight-dark.rasi create mode 100644 pkgs/beos-r5-icons/default.nix create mode 100644 pkgs/haiku-cursor/default.nix diff --git a/flake.nix b/flake.nix index 972706b..976f3fc 100644 --- a/flake.nix +++ b/flake.nix @@ -81,6 +81,7 @@ ./home/sscoble.nix ./home/pi.nix ./home/wgu.nix + ./home/macos.nix { home.homeDirectory = "/Users/sscoble"; nixpkgs.config.allowUnfree = true; diff --git a/home/bin/lid-handler.sh b/home/bin/lid-handler.sh new file mode 100755 index 0000000..05318ad --- /dev/null +++ b/home/bin/lid-handler.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# ~/.config/hypr/scripts/lid-handler.sh +# Handles laptop lid close/open events for Hyprland. +# Called by Hyprland bindl (runs in user session context). +# Usage: lid-handler.sh close|open + +set -euo pipefail + +EVENT="${1:-}" # "close" or "open" + +# Determine lid state if not provided +if [ -z "$EVENT" ]; then + LID_STATE=$(cat /proc/acpi/button/lid/LID0/state 2>/dev/null || cat /proc/acpi/button/lid/LID/state 2>/dev/null || echo "open") + if echo "$LID_STATE" | grep -qi "closed"; then + EVENT="close" + else + EVENT="open" + fi +fi + +# Detect monitors via hyprctl +INTERNAL=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.name | startswith("eDP")) | .name' | head -1 || echo "") +EXTERNAL=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.name | startswith("eDP") | not) | .name' | head -1 || echo "") + +# Fallback: try known names from hyprland.conf +[ -z "$INTERNAL" ] && INTERNAL="eDP-1" +[ -z "$EXTERNAL" ] && EXTERNAL="DP-3" + +case "$EVENT" in + close) + if [ -n "$EXTERNAL" ]; then + # Move all workspaces to the external display first + WORKSPACES=$(hyprctl workspaces -j 2>/dev/null | jq -r '.[].id' || echo "") + for ws in $WORKSPACES; do + hyprctl dispatch moveworkspacetomonitor "$ws" "$EXTERNAL" 2>/dev/null || true + done + # Set external as primary (scale factor 1) + hyprctl keyword monitor "$EXTERNAL",preferred,auto,1 2>/dev/null || true + # Disable internal display + if [ -n "$INTERNAL" ]; then + hyprctl keyword monitor "$INTERNAL,disable" 2>/dev/null || true + fi + notify-send -t 3000 "Lid Closed" "Switched to $EXTERNAL as primary display" + else + notify-send -t 3000 "Lid Closed" "No external display detected — locking" + hyprlock 2>/dev/null || true + fi + ;; + open) + # Re-enable internal display alongside external + if [ -n "$INTERNAL" ]; then + hyprctl keyword monitor "$INTERNAL,preferred,auto,1" 2>/dev/null || true + notify-send -t 3000 "Lid Opened" "Internal display $INTERNAL re-enabled" + fi + ;; +esac diff --git a/home/desktop.nix b/home/desktop.nix index 822382e..f2e5857 100644 --- a/home/desktop.nix +++ b/home/desktop.nix @@ -5,6 +5,8 @@ home.packages = with pkgs; [ # pi + qmd (from overlay) qmd + # Browser + chromium # Terminal ghostty zellij @@ -19,6 +21,8 @@ awww btop fuzzel + # Screen recording / streaming + obs-studio # Lock + idle hyprlock hypridle @@ -40,9 +44,13 @@ bibata-cursors papirus-icon-theme adw-gtk3 + beos-r5-icons + haiku-cursor # Network / containers / system networkmanagerapplet - firefox + gh + winetricks + cabextract # required by winetricks for .cab extraction tailscale docker dive @@ -53,24 +61,58 @@ nmap tcpdump speedtest-cli + protonmail-bridge-gui ]; + # === Proton Mail Bridge user service (starts at login) === + systemd.user.services.protonmail-bridge = { + Unit = { + Description = "Proton Mail Bridge"; + After = [ "network-online.target" "graphical-session.target" ]; + Wants = [ "network-online.target" ]; + }; + + Service = { + Type = "simple"; + ExecStart = "${pkgs.protonmail-bridge}/bin/protonmail-bridge --non-interactive"; + Restart = "on-failure"; + RestartSec = "5"; + }; + + Install = { + WantedBy = [ "graphical-session.target" ]; + }; + }; + # === GTK / QT theming === gtk = { enable = true; theme.name = "adw-gtk3-dark"; - iconTheme.name = "Papirus-Dark"; + iconTheme.name = "BeOS-r5"; cursorTheme = { - name = "Bibata-Modern-Classic"; + name = "Haiku_Cursor"; size = 24; }; + gtk3.extraConfig = { + gtk-application-prefer-dark-theme = 1; + }; + gtk4.extraConfig = { + gtk-application-prefer-dark-theme = 1; + }; }; qt = { enable = true; - platformTheme.name = "gtk"; + platformTheme.name = "gtk3"; style.name = "adwaita-dark"; }; + # === libadwaita dark mode === + dconf.settings = { + "org/gnome/desktop/interface" = { + color-scheme = "prefer-dark"; + }; + }; + # === Hyprland WM config === home.file.".config/hypr/hyprland.conf" = { force = true; @@ -165,7 +207,7 @@ 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" "temperature" "memory" ]; + modules-right = [ "tray" "bluetooth" "pulseaudio" "backlight" "battery" "network" "cpu" "temperature" "memory" ]; "hyprland/workspaces" = { format = "{icon}"; @@ -175,7 +217,7 @@ }; persistent_workspaces = { "1" = []; "2" = []; "3" = []; "4" = []; "5" = []; }; }; - clock = { format = "{:%a %b %d %H:%M}"; tooltip-format = "{:%Y %B}\n{calendar}"; }; + clock = { format = "{:%a %b %d %I:%M %p}"; tooltip-format = "{:%Y %B}\n{calendar}"; }; cpu = { format = " {usage}%"; }; temperature = { format = " {temperatureC}°C"; interval = 10; thermal-zone = 5; }; memory = { format = "󰍛 {}%"; }; @@ -193,6 +235,15 @@ format = "{icon} {volume}%"; format-bluetooth = "{icon}󰂯 {volume}%"; format-muted = "󰝟"; format-icons.default = [ "󰕿" "󰖀" "󰕾" ]; on-click = "pavucontrol"; }; + bluetooth = { + format = "󰂯"; + format-connected = "󰂯 {device_alias}"; + format-disabled = ""; + format-no-controller = ""; + format-off = "󰂲"; + tooltip-format = "{device_alias} — {device_address}"; + on-click = "blueman-manager"; + }; tray.spacing = 10; }; }; @@ -204,7 +255,7 @@ #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; } + #clock, #cpu, #memory, #backlight, #battery, #network, #pulseaudio, #bluetooth, #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; } @@ -213,7 +264,7 @@ # === Rofi launcher === home.file.".config/rofi/config.rasi" = { force = true; - source = ./rofi-config.rasi; + source = ./spotlight-dark.rasi; }; # === Dunst notifications === home.file.".config/dunst/dunstrc".text = '' @@ -230,8 +281,110 @@ [urgency_normal] background = "#1e1e2e"; foreground = "#cdd6f4"; timeout = 5 [urgency_critical] background = "#f38ba8"; foreground = "#1e1e2e"; timeout = 0 ''; + # === Lid handler === + home.file.".config/hypr/scripts/lid-handler.sh" = { + force = true; + executable = true; + source = ./bin/lid-handler.sh; + }; + # === Wallpaper rotation === home.file.".local/bin/wallpaper-rotate" = { + force = true; source = ./wallpaper-rotate.sh; }; + + # === BeOS cursor theme installer === + home.file.".local/bin/install-beos-cursor" = { + force = true; + executable = true; + text = '' + #!/usr/bin/env bash + set -euo pipefail + + CURSOR_NAME="BeOS" + ICONS_DIR="$HOME/.local/share/icons" + CURSOR_DIR="$ICONS_DIR/$CURSOR_NAME" + + echo "==> BeOS Cursor Installer" + echo "" + + if [ -d "$CURSOR_DIR" ] && [ -f "$CURSOR_DIR/index.theme" ]; then + echo " ✅ BeOS cursor already installed at $CURSOR_DIR" + echo "" + echo "To switch to it:" + echo " gsettings set org.gnome.desktop.interface cursor-theme BeOS" + echo " # Or update ~/.config/gtk-3.0/settings.ini:" + echo " # gtk-cursor-theme-name=BeOS" + exit 0 + fi + + echo "To install BeOS cursors:" + echo " 1. Open https://www.gnome-look.org/p/999293/ in your browser" + echo " 2. Download the BeOS-Cursors.tar.gz" + echo " 3. Run: install-beos-cursor /path/to/BeOS-Cursors.tar.gz" + echo "" + + if [ $# -eq 0 ]; then + echo "No file provided. See instructions above." + exit 1 + fi + + ARCHIVE="$1" + if [ ! -f "$ARCHIVE" ]; then + echo " ❌ File not found: $ARCHIVE" + exit 1 + fi + + echo "==> Installing BeOS cursor from $ARCHIVE..." + mkdir -p "$ICONS_DIR" + tar xzf "$ARCHIVE" -C "$ICONS_DIR" 2>/dev/null || tar xzf "$ARCHIVE" -C "$ICONS_DIR" --strip-components=1 2>/dev/null || true + + if [ -f "$CURSOR_DIR/index.theme" ]; then + echo " ✅ Installed to $CURSOR_DIR" + echo "" + echo "Now switch to BeOS cursor:" + echo " gsettings set org.gnome.desktop.interface cursor-theme BeOS" + else + # Try to find what was extracted + echo " Checking extracted directories..." + find "$ICONS_DIR" -name "index.theme" -exec dirname {} \\; 2>/dev/null | head -5 + echo "" + echo "The extracted cursor directories are listed above." + fi + ''; + }; + + # === Logos Bible Software (Wine) === + + # Helper script: first run installs Logos, subsequent runs launch it + home.file.".local/bin/logos" = { + force = true; + executable = true; + text = '' + #!/usr/bin/env bash + # Launch Logos Bible Software as a Chromium web app + exec chromium --app="https://app.logos.com" --name="Logos" "$@" + ''; + }; + + # Logos web app icon + home.file.".local/share/icons/hicolor/scalable/apps/logos.svg".source = ./logos-icon.svg; + + # Desktop entry for Logos web app + home.file.".local/share/applications/logos.desktop" = { + force = true; + text = '' + [Desktop Entry] + Type=Application + Name=Logos Bible Software + Comment=Logos Bible Software (Web App) + Exec=logos + Icon=logos + Terminal=false + Categories=Office;Education;Network; + StartupWMClass=logos + MimeType=x-scheme-handler/logos; + ''; + }; } diff --git a/home/hyprland.conf b/home/hyprland.conf index 4c79240..365c562 100644 --- a/home/hyprland.conf +++ b/home/hyprland.conf @@ -4,7 +4,7 @@ monitor=,preferred,auto,1 monitor=DP-3,preferred,auto,1.5 env = XCURSOR_SIZE,24 -env = XCURSOR_THEME,Bibata-Modern-Classic +env = XCURSOR_THEME,Haiku_Cursor env = GTK_THEME,adw-gtk3-dark env = QT_QPA_PLATFORMTHEME,gtk2 @@ -14,11 +14,12 @@ exec-once = wl-paste --type text --watch cliphist store exec-once = wl-paste --type image --watch cliphist store exec-once = hypridle exec-once = ~/.local/bin/wallpaper-rotate +exec-once = blueman-applet -# Auto-launch apps into their workspaces -exec-once = [workspace 1 silent] ghostty -e zellij -exec-once = [workspace 3 silent] firefox -exec-once = [workspace 4 silent] discord +# Auto-launch apps (window rules place them in correct workspaces) +exec-once = ghostty -e zellij +exec-once = firefox +exec-once = discord input { kb_layout = us @@ -85,9 +86,11 @@ bind = $mainMod SHIFT, Return, exec, ghostty bind = $mainMod, B, exec, firefox bind = $mainMod, D, exec, fuzzel bind = $mainMod, Space, exec, fuzzel +bind = $mainMod SHIFT, Space, exec, rofi -show drun bind = $mainMod, V, exec, cliphist list | rofi -dmenu | cliphist decode | wl-copy bind = $mainMod, N, exec, thunar bind = $mainMod, L, exec, hyprlock +bind = $mainMod, X, exec, blueman-manager bind = $mainMod, Q, killactive bind = $mainMod, F, fullscreen bind = $mainMod, G, togglefloating @@ -137,6 +140,10 @@ bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%- bind = $mainMod SHIFT, Q, exec, hyprctl dispatch exit bind = $mainMod SHIFT, R, exec, hyprctl reload +# Lid switch: close → external display only, open → restore internal +bindl = , switch:on:Lid Switch, exec, ~/.config/hypr/scripts/lid-handler.sh close +bindl = , switch:off:Lid Switch, exec, ~/.config/hypr/scripts/lid-handler.sh open + windowrule = match:class ^(pavucontrol)$, float on windowrule = match:class ^(firefox)$, match:title PIP, float on diff --git a/home/logos-icon.svg b/home/logos-icon.svg new file mode 100644 index 0000000..220fe8e --- /dev/null +++ b/home/logos-icon.svg @@ -0,0 +1,4 @@ + + + L + diff --git a/home/macos.nix b/home/macos.nix new file mode 100644 index 0000000..b900388 --- /dev/null +++ b/home/macos.nix @@ -0,0 +1,270 @@ +{ config, pkgs, lib, ... }: + +{ + # === macOS-specific configuration === + + home.packages = with pkgs; [ + # macOS window manager + aerospace + + # Keyboard remapping (GUI app — download separately or install via brew) + # brew install --cask karabiner-elements + ]; + + # ============================================================================= + # KEYBOARD GOAL: Consistent finger positions across keyboards + # + # ThinkPad T490 (Linux): Ctrl | Super(Win) | Alt | Space + # Keychron V10M (macOS): Ctrl | Alt(⌥) | Cmd | Space ← remapped + # After remap (macOS): Ctrl | Super(Cmd) | Alt | Space + # + # Muscle memory: + # Middle key → Super/Cmd = WM actions (workspaces, focus, move) + # Next to space → Alt/Option = app shortcuts + # + # Remap: Physical Alt(⌥) → Cmd(⌘), Physical Cmd(⌘) → Alt(⌥) + # Applied via Karabiner-Elements, targeted to Keychron only. + # + # To find your Keychron's vendor/product IDs: + # 1. Open Karabiner-Elements → EventViewer tab + # 2. Press a key on the Keychron + # 3. Note the "VendorID" and "ProductID" values + # 4. Update them in the JSON below + # + # ============================================================================= + + home.file.".config/karabiner/karabiner.json" = { + force = true; + text = builtins.toJSON { + global = { + check_for_updates_on_startup = false; + show_in_menu_bar = false; + show_profile_name_in_menu_bar = false; + }; + profiles = [ + { + name = "Default"; + selected = true; + simple_modifications = []; + fn_function_keys = []; + virtual_hid_keyboard = { + keyboard_type = "ansi"; + caps_lock_delay_microseconds = 0; + }; + devices = []; + complex_modifications = { + parameters = { + "basic.simultaneous_threshold_milliseconds" = 50; + "basic.to_if_held_down_threshold_milliseconds" = 500; + "basic.to_delayed_action_delay_milliseconds" = 500; + "basic.to_if_alone_timeout_milliseconds" = 1000; + }; + rules = [ + { + description = "Keychron V10M: swap Option ↔ Command (match ThinkPad bottom row)"; + manipulators = [ + { + # Physical Alt(⌥) → sends Cmd(⌘) = Super/WM key + type = "basic"; + from = { + key_code = "left_alt"; + modifiers = { optional = [ "any" ]; }; + }; + to = [ { key_code = "left_gui"; } ]; + conditions = [ + { + type = "device_if"; + identifiers = [ + { + # ⚠ UPDATE THESE with your Keychron's IDs + vendor_id = 0; + product_id = 0; + description = "Keychron V10M (Alice 75)"; + } + ]; + } + ]; + } + { + # Physical Cmd(⌘) → sends Alt(⌥) = Alt key + type = "basic"; + from = { + key_code = "left_gui"; + modifiers = { optional = [ "any" ]; }; + }; + to = [ { key_code = "left_alt"; } ]; + conditions = [ + { + type = "device_if"; + identifiers = [ + { + vendor_id = 0; + product_id = 0; + } + ]; + } + ]; + } + ]; + } + ]; + }; + } + ]; + }; + }; + + # ============================================================================= + # AEROSPACE — macOS Tiling Window Manager + # + # Keybindings use `cmd` (Super after remap) to match Hyprland's SUPER: + # Middle key + number → switch workspace + # Middle key + h/j/k/l → focus direction + # Middle key + tab → focus next window (cycle through all workspaces) + # Middle key + shift → move window + # + # macOS-native Cmd+Tab (now Alt+Tab after remap) still cycles apps. + # ============================================================================= + + home.file.".config/aerospace/aerospace.toml" = { + force = true; + text = '' + # ============================================================================= + # Aerospace — macOS Tiling Window Manager + # + # Keybinding conventions (after Keychron remap matches ThinkPad): + # cmd (middle key) = SUPER — WM actions + # alt (next to space) = ALT — app shortcuts + # ============================================================================= + + # --- General --- + [general] + border = true + gaps = { + inner = { px = 6, vertical = 8, horizontal = 8 }, + outer = { px = 4, px-monitor-workspace = 4, bottom = 6 }, + } + + # ============================================================================= + # WORKSPACE SWITCHING — cmd = middle key + # ============================================================================= + [mode.main.binding] + # Switch to workspace + cmd-1 = "workspace 1" + cmd-2 = "workspace 2" + cmd-3 = "workspace 3" + cmd-4 = "workspace 4" + cmd-5 = "workspace 5" + cmd-6 = "workspace 6" + cmd-7 = "workspace 7" + cmd-8 = "workspace 8" + cmd-9 = "workspace 9" + + # Move window to workspace + cmd-shift-1 = "move-node-to-workspace 1" + cmd-shift-2 = "move-node-to-workspace 2" + cmd-shift-3 = "move-node-to-workspace 3" + cmd-shift-4 = "move-node-to-workspace 4" + cmd-shift-5 = "move-node-to-workspace 5" + cmd-shift-6 = "move-node-to-workspace 6" + cmd-shift-7 = "move-node-to-workspace 7" + cmd-shift-8 = "move-node-to-workspace 8" + cmd-shift-9 = "move-node-to-workspace 9" + + # ============================================================================= + # FOCUS NAVIGATION — Vim-style with cmd (middle key) + # ============================================================================= + cmd-h = "focus left" + cmd-j = "focus down" + cmd-k = "focus up" + cmd-l = "focus right" + + # Tab through windows across all workspaces + cmd-tab = "focus next" + + # ============================================================================= + # WINDOW MANAGEMENT + # ============================================================================= + cmd-f = "fullscreen" + cmd-w = "close" + cmd-t = "floating toggle" + cmd-d = "layout tiles horizontal vertical" + cmd-shift-d = "layout tiles vertical horizontal" + + # ============================================================================= + # LAYOUT MANAGEMENT + # ============================================================================= + cmd-r = "mode resize" + + cmd-shift-h = "move left" + cmd-shift-j = "move down" + cmd-shift-k = "move up" + cmd-shift-l = "move right" + + # ============================================================================= + # RESIZE MODE + # ============================================================================= + [mode.resize.binding] + cmd-h = "resize -50 0" + cmd-j = "resize 0 50" + cmd-k = "resize 0 -50" + cmd-l = "resize 50 0" + escape = "mode main" + enter = "mode main" + + # ============================================================================= + # MISC + # ============================================================================= + cmd-shift-period = "reload-config" + cmd-backslash = "layout v_accordion" + cmd-shift-slash = "layout h_accordion" + + # ============================================================================= + # APP CYCLING + # + # macOS natively handles app cycling via Cmd+Tab. + # After Karabiner remap, the physical key next to spacebar now sends Alt, + # so what was Cmd+Tab is now available as a distinct chord. + # We use cmd+tab (middle key + tab) for Aerospace's "focus next" above, + # and the macOS native cycle is still accessible via alt+tab (next-to-space + tab). + # ============================================================================= + ''; + }; + + # ============================================================================= + # HELPER: Find Keychron keyboard IDs for Karabiner + # ============================================================================= + home.file.".local/bin/find-keychron-id" = { + force = true; + executable = true; + text = '' + #!/usr/bin/env bash + # Find your Keychron's vendor/product IDs for Karabiner-Elements config. + # + # Usage: ./find-keychron-id + # + # If Karabiner EventViewer is running, check its log for recent key events. + LOG="$HOME/.local/share/karabiner/eventviewer.log" + if [ -f "$LOG" ]; then + echo "Checking Karabiner EventViewer log..." + grep -i "keychron\|vendor_id\|product_id" "$LOG" 2>/dev/null | tail -10 + fi + + # Alternative: use ioreg to list HID devices + echo "" + echo "All HID keyboard devices (look for Keychron):" + ioreg -p IOUSB -l -w 0 | grep -A 10 -i "keyboard\|keychron" 2>/dev/null || \\ + ioreg -r -c IOUSBDevice -l -w 0 | grep -A 15 -i "keychron\|keyboard" | grep -E "vendor|product|idProduct|idVendor|USB Product Name" | head -20 + + echo "" + echo "Or press a key on your Keychron and run:" + echo " hidutil list 2>/dev/null | grep -i keychron" + echo "" + echo "Then update vendor_id and product_id (as DECIMAL numbers) in:" + echo " ~/.config/karabiner/karabiner.json" + echo "" + echo " e.g. if vendor_id=0x3434, product_id=0x0408" + echo " then in JSON: vendor_id=13364, product_id=1032" + ''; + }; +} diff --git a/home/spotlight-dark.rasi b/home/spotlight-dark.rasi new file mode 100644 index 0000000..9f5ca38 --- /dev/null +++ b/home/spotlight-dark.rasi @@ -0,0 +1,150 @@ +/* Spotlight-Dark — macOS-inspired Rofi theme */ + +configuration { + modi: "drun,run,window"; + show-icons: true; + icon-theme: "Papirus-Dark"; + drun-display-format: "{name}"; + font: "JetBrainsMono Nerd Font 14"; + matching: "fuzzy"; + terminal: "ghostty"; + fuzzy-match: true; + scrollbar: false; + sidebar-mode: false; +} + +* { + bg-col: rgba(30, 30, 46, 0.93); + bg-alt: rgba(49, 50, 68, 0.95); + fg-col: rgba(205, 214, 244, 1.0); + fg-dim: rgba(205, 214, 244, 0.5); + sel-bg: rgba(137, 180, 250, 0.18); + urgent-col: rgba(243, 139, 168, 1.0); + border-col: rgba(137, 180, 250, 0.0); +} + +window { + width: 520px; + location: center; + anchor: center; + fullscreen: false; + y-offset: -120px; + + border: 0px; + border-radius: 14px; + background-color: @bg-col; + text-color: @fg-col; + padding: 0; +} + +mainbox { + padding: 0; + children: [ inputbar, listview ]; + spacing: 0; +} + +inputbar { + padding: 14px 18px; + background-color: @bg-alt; + border-radius: 14px 14px 0 0; + margin: 0; + children: [ entry, case-indicator ]; + spacing: 8; +} + +entry { + background: transparent; + text-color: @fg-col; + caret-color: @fg-col; + placeholder-color: @fg-dim; + placeholder: "Search apps, commands..."; + expand: true; + horizontal-align: 0; + vertical-align: 0.5; + font: "JetBrainsMono Nerd Font 14"; +} + +case-indicator { + background: transparent; + text-color: @fg-dim; + font: "JetBrainsMono Nerd Font 10"; + spacing: 0; +} + +listview { + background: transparent; + padding: 6px 0; + lines: 8; + columns: 1; + spacing: 0; + cycle: true; + dynamic: true; + scrollbar: false; + layout: vertical; +} + +element { + padding: 10px 18px; + border-radius: 0; + text-color: @fg-col; + background-color: transparent; +} + +element normal.normal { + background-color: transparent; + text-color: @fg-col; +} + +element selected.normal { + background-color: @sel-bg; + text-color: @fg-col; +} + +element urgent.normal { + text-color: @urgent-col; +} + +element-icon { + size: 22px; + padding: 0 12px 0 0; + horizontal-align: 0; + vertical-align: 0.5; +} + +element-text { + padding: 0; + vertical-align: 0.5; + horizontal-align: 0; + text-color: inherit; + font: "JetBrainsMono Nerd Font 12"; +} + +element-text selected { + text-color: @fg-col; +} + +message { + margin: 0; + padding: 10px 18px; + border-radius: 0 0 14px 14px; + background-color: @bg-alt; + text-color: @fg-dim; +} + +textbox { + padding: 4px 8px; + text-color: @fg-dim; +} + +button { + padding: 4px; + border-radius: 4px; + text-color: @fg-col; +} + +scrollbar { + width: 4px; + border: 0; + handle-width: 4px; + handle-color: @border-col; +} diff --git a/home/sscoble.nix b/home/sscoble.nix index 9911ca6..76cbb8b 100644 --- a/home/sscoble.nix +++ b/home/sscoble.nix @@ -45,20 +45,22 @@ # === Git Config === programs.git = { enable = true; - userName = "Scott Scoble"; - userEmail = "sscoble@scobleclaw.dev"; - extraConfig = { + settings = { + user = { + name = "Scott Scoble"; + email = "sscoble@scobleclaw.dev"; + }; init.defaultBranch = "main"; pull.rebase = true; push.autoSetupRemote = true; core.editor = "nvim"; - }; - aliases = { - lol = "log --graph --oneline --decorate --all"; - co = "checkout"; - br = "branch"; - st = "status"; - ci = "commit"; + alias = { + lol = "log --graph --oneline --decorate --all"; + co = "checkout"; + br = "branch"; + st = "status"; + ci = "commit"; + }; }; }; @@ -156,7 +158,7 @@ l = "ls -CF"; nix-clean = "sudo nix-collect-garbage -d && nix-collect-garbage -d"; }; - initExtra = '' + initContent = '' # 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" diff --git a/home/wgu.nix b/home/wgu.nix index acc1cb0..c802ccf 100644 --- a/home/wgu.nix +++ b/home/wgu.nix @@ -8,7 +8,6 @@ # Browsers brave - firefox # Note-taking obsidian diff --git a/modules/hyprland.nix b/modules/hyprland.nix index 917b6c8..4aa38d7 100644 --- a/modules/hyprland.nix +++ b/modules/hyprland.nix @@ -10,6 +10,49 @@ # Session variables environment.sessionVariables.NIXOS_OZONE_WL = "1"; + # === Lid close behavior === + # Don't suspend when lid closes — external display stays active + services.logind = { + lidSwitch = "ignore"; + lidSwitchExternalPower = "ignore"; + lidSwitchDocked = "ignore"; + }; + + # Fallback ACPI handler for lid events outside Hyprland session + # Primary handling is done via Hyprland bindl in hyprland.conf + services.acpid = { + enable = true; + handlers.lid = { + event = "button/lid.*"; + action = '' + ${pkgs.bash}/bin/bash -c ' + LID_STATE=$(cat /proc/acpi/button/lid/LID0/state 2>/dev/null || cat /proc/acpi/button/lid/LID/state 2>/dev/null || echo "open") + if echo "$LID_STATE" | grep -qi "closed"; then + EVENT="close" + else + EVENT="open" + fi + for user_dir in /run/user/*/hypr/; do + [ -d "$user_dir" ] || continue + SIG=$(ls "$user_dir" 2>/dev/null | head -1) + [ -z "$SIG" ] && continue + UID=$(basename $(dirname "$user_dir")) + USER=$(id -nu $UID 2>/dev/null || echo "sscoble") + HOME_DIR=$(getent passwd "$USER" | cut -d: -f6) + USER_SCRIPT="$HOME_DIR/.config/hypr/scripts/lid-handler.sh" + if [ -x "$USER_SCRIPT" ]; then + sudo -u "$USER" \ + XDG_RUNTIME_DIR="/run/user/$UID" \ + HYPRLAND_INSTANCE_SIGNATURE="$SIG" \ + "$USER_SCRIPT" "$EVENT" + fi + break + done + ' + ''; + }; + }; + # Security — required for hyprlock security.pam.services.hyprlock = {}; @@ -82,6 +125,15 @@ hardware.bluetooth.enable = true; hardware.bluetooth.powerOnBoot = true; + # Bluetooth manager (GUI pairing, tray applet) + # Note: using manual setup instead of services.blueman.enable to avoid + # a systemd unit bug (duplicate ExecStart in blueman-applet.service). + # The applet is launched via exec-once in hyprland.conf instead. + environment.systemPackages = with pkgs; [ blueman ]; + services.dbus.packages = with pkgs; [ blueman ]; + systemd.user.services.blueman-applet = { enable = false; }; + systemd.user.services.blueman-manager = { enable = false; }; + # NetworkManager applet programs.nm-applet.enable = true; } diff --git a/modules/laptop.nix b/modules/laptop.nix index 6d9ad7d..42a8f0c 100644 --- a/modules/laptop.nix +++ b/modules/laptop.nix @@ -43,8 +43,15 @@ wineWow64Packages.stable ]; - # DNS fallback (Tailscale MagicDNS sometimes blocks public resolvers) - networking.nameservers = [ "1.1.1.1" "8.8.8.8" ]; + # === WiFi / NetworkManager (imperative — not idempotent) === + # We move between SSIDs regularly, so NO declarative wifi networks here. + # Use nm-applet (tray) or nmcli (CLI) to connect to any SSID at runtime. + # Connections persist across reboots via /etc/NetworkManager/system-connections/ + networking.networkmanager.enable = true; + + # DNS handed out by NM to each connection; Tailscale MagicDNS can interfere + # with public resolvers, so we inject Cloudflare + Google as fallbacks. + networking.networkmanager.insertNameservers = [ "1.1.1.1" "8.8.8.8" ]; # Passwordless sudo for sscoble security.sudo.extraRules = [ diff --git a/modules/printing.nix b/modules/printing.nix index 5531626..9dc4d6b 100644 --- a/modules/printing.nix +++ b/modules/printing.nix @@ -20,7 +20,7 @@ # Enable network printer discovery via mDNS/Bonjour services.avahi = { enable = true; - nssmdns = true; + nssmdns4 = true; publish = { enable = true; addresses = true; diff --git a/modules/t490.nix b/modules/t490.nix index 15d20a0..6d50f29 100644 --- a/modules/t490.nix +++ b/modules/t490.nix @@ -63,7 +63,51 @@ # === powertop === powerManagement.powertop.enable = true; - # Skip thermald — conflicts with throttled (enabled by nixos-hardware) + # === throttled — undervolt + raise trip temp for better sustained perf === + services.throttled = { + extraConfig = '' + [GENERAL] + Enabled: True + Sysfs_Power_Path: /sys/class/power_supply/AC*/online + Autoreload: True + + [BATTERY] + Update_Rate_s: 30 + PL1_Tdp_W: 29 + PL1_Duration_s: 28 + PL2_Tdp_W: 29 + PL2_Duration_S: 0.002 + Trip_Temp_C: 85 + cTDP: 0 + Disable_BDPROCHOT: False + + [AC] + Update_Rate_s: 5 + PL1_Tdp_W: 44 + PL1_Duration_s: 28 + PL2_Tdp_W: 44 + PL2_Duration_S: 0.002 + Trip_Temp_C: 97 + cTDP: 0 + Disable_BDPROCHOT: False + + [UNDERVOLT.BATTERY] + CORE: -70 + GPU: -50 + CACHE: -70 + UNCORE: -40 + ANALOGIO: -40 + + [UNDERVOLT.AC] + CORE: -80 + GPU: -50 + CACHE: -80 + UNCORE: -50 + ANALOGIO: -50 + ''; + }; + + # Skip thermald — conflicts with throttled services.thermald.enable = false; # === zram swap (faster + saves NVMe writes) === @@ -135,11 +179,14 @@ # === Tailscale (consistent cluster access from any network) === services.tailscale.enable = true; + # === Proton Mail Bridge (local IMAP/SMTP for Proton Mail) === + # The systemd user service is defined in home/desktop.nix + # === NVMe tools === - environment.systemPackages = with pkgs; [ nvme-cli ]; + environment.systemPackages = with pkgs; [ nvme-cli protonmail-bridge ]; # === Touchpad & TrackPoint === - services.xserver.libinput = { + services.libinput = { enable = true; touchpad = { tapping = true; diff --git a/pi-overlay.nix b/pi-overlay.nix index 29e8f78..e908b38 100644 --- a/pi-overlay.nix +++ b/pi-overlay.nix @@ -2,4 +2,7 @@ final: prev: { qmd = final.writeShellScriptBin "qmd" '' exec ${final.bun}/bin/bunx --bun @tobilu/qmd "$@" ''; + + beos-r5-icons = final.callPackage ./pkgs/beos-r5-icons { }; + haiku-cursor = final.callPackage ./pkgs/haiku-cursor { }; } diff --git a/pkgs/beos-r5-icons/default.nix b/pkgs/beos-r5-icons/default.nix new file mode 100644 index 0000000..7da88b7 --- /dev/null +++ b/pkgs/beos-r5-icons/default.nix @@ -0,0 +1,34 @@ +{ lib, stdenvNoCC, fetchFromGitHub }: + +stdenvNoCC.mkDerivation { + pname = "beos-r5-icons"; + version = "2021-03-24"; + + src = fetchFromGitHub { + owner = "jcherven"; + repo = "BeOS-r5-Icons"; + rev = "1c1067eabaeb0196c12606ac19b2aed4496bc15e"; + hash = "sha256-6qnOWMdwgK9tKifHXSF6s1KjHkdIG/jnM9Te43cVOjo="; + }; + + installPhase = '' + mkdir -p $out/share/icons/BeOS-r5 + cp -pr * $out/share/icons/BeOS-r5/ + # Remove broken symlinks (icon theme has some dangling links) + find $out/share/icons/BeOS-r5 -type l -not -exec test -e {} \; -delete + rm -f $out/share/icons/BeOS-r5/screenshot.png + rm -f $out/share/icons/BeOS-r5/.icon-theme.cache + ''; + + meta = with lib; { + description = "Classic BeOS r5 icons for Linux desktops"; + longDescription = '' + A large collection of BeOS & period-correct icons. + Includes icons from BeOS-r5, Zeta OS, Haiku OS, KDEclassic, and more. + ''; + homepage = "https://github.com/jcherven/BeOS-r5-Icons"; + license = licenses.gpl2Plus; + platforms = platforms.linux; + maintainers = [ ]; + }; +} diff --git a/pkgs/haiku-cursor/default.nix b/pkgs/haiku-cursor/default.nix new file mode 100644 index 0000000..45f9b2e --- /dev/null +++ b/pkgs/haiku-cursor/default.nix @@ -0,0 +1,28 @@ +{ lib, stdenvNoCC, fetchurl }: + +stdenvNoCC.mkDerivation { + pname = "haiku-cursor"; + version = "linux-02"; + + src = fetchurl { + url = "https://github.com/leath-dub/Haiku_Cursor/releases/download/linux-02/Haiku_Cursor.tgz"; + hash = "sha256-AXgo+jqrCjEyDpWL6GnpUAbvx3Oje+7zQvnIC8j1uz0="; + }; + + installPhase = '' + mkdir -p $out/share/icons/Haiku_Cursor + cp -pr * $out/share/icons/Haiku_Cursor/ + ''; + + meta = with lib; { + description = "Haiku/BeOS-style XCursor theme"; + longDescription = '' + A port of the Haiku cursor set as an XCursor theme. + These cursors are faithful to the BeOS/Haiku look. + ''; + homepage = "https://github.com/leath-dub/Haiku_Cursor"; + license = licenses.mit; + platforms = platforms.linux; + maintainers = [ ]; + }; +}