diff --git a/.chezmoi.yaml.tmpl b/.chezmoi.yaml.tmpl index a8b4a2a..263965e 100644 --- a/.chezmoi.yaml.tmpl +++ b/.chezmoi.yaml.tmpl @@ -60,4 +60,22 @@ age: data: os_family: {{ $osFamily | quote }} - os_id: {{ .chezmoi.osRelease.id | quote }} \ No newline at end of file + os_id: {{ .chezmoi.osRelease.id | quote }} + + # Sway/Wayland desktop stack — enabled per host. Pis don't need it + # (headless); x86_64 desktops do. Override at runtime by creating + # `~/.config/chezmoi/features/sway` (force ON) or + # `~/.config/chezmoi/features/no-sway` (force OFF). + # + # To toggle for a new box: `touch ~/.config/chezmoi/features/sway` + # then `chezmoi apply`. + {{- $swayOverrideOn := stat (joinPath .chezmoi.homeDir ".config/chezmoi/features/sway") }} + {{- $swayOverrideOff := stat (joinPath .chezmoi.homeDir ".config/chezmoi/features/no-sway") }} + {{- if $swayOverrideOn }} + sway: true + {{- else if $swayOverrideOff }} + sway: false + {{- else }} + {{- $swayHosts := list "miche" "byte" "kaiser" }} + sway: {{ has .chezmoi.hostname $swayHosts }} + {{- end }} \ No newline at end of file diff --git a/dot_config/foot/foot.ini b/dot_config/foot/foot.ini new file mode 100644 index 0000000..9e1ce53 --- /dev/null +++ b/dot_config/foot/foot.ini @@ -0,0 +1,88 @@ +# foot terminal — gruvbox dark, Maple Mono 14pt with Terminus fallback +# Reference: man foot.ini + +[main] +font=Maple Mono CN:size=14,Terminus:size=14 +font-bold=Maple Mono CN:weight=bold:size=14 +font-italic=Maple Mono CN:slant=italic:size=14 +font-bold-italic=Maple Mono CN:weight=bold:slant=italic:size=14 +font-size-adjustment=0.5 +pad=8x8 +term=foot +# (Previously set to "foot-extra", but that terminfo is not installed in the +# CachyOS foot package; only foot, foot+base, foot-direct are. Setting +# TERM=foot-extra breaks every ncurses program — htop, nvtop, vim, etc. — +# with "cannot initialize terminal type". "foot" has 256 colors and +# everything modern tools need.) +# kaiser runs zsh as the login shell (SHELL=/bin/fish was misleading — that +# comes from sway's inherited env, not the user's actual shell). Hardcode +# /usr/bin/zsh here. foot does NOT expand $SHELL in the shell= value (it +# passes the literal string to its child), so don't try the sh,-c trick — +# it just makes foot spawn a binary literally named "${SHELL:-/bin/bash}" +# and exit with "No such file or directory". +shell=/usr/bin/zsh + +# PTY +# utmphelper is not packaged with foot on this CachyOS build. Leave it unset +# to disable utmp logging for foot sessions; harmless and not required. + +# Initial geometry +initial-window-size-chars=120x32 + +[scrollback] +lines=10000 +indicator-position=relative + +[cursor] +style=block +blink=yes +blink-rate=500 +beam-thickness=2px + +[mouse] +hide-when-typing=yes +alternate-scroll-mode=true + +[colors-dark] +# Gruvbox dark palette (gruvbox-community) — bg0_h / fg / 16 ANSI +# https://github.com/gruvbox-community/gruvbox +background=1d2021 +foreground=ebdbb2 +# Slight transparency — 0.90 alpha. Foot 1.27+ uses a separate alpha key +# (in [colors-dark], not 8-digit hex — that was foot 1.24 syntax). +alpha=0.90 + +# Regular ANSI (0-7) +regular0=282828 +regular1=cc241d +regular2=98971a +regular3=d79921 +regular4=458588 +regular5=b16286 +regular6=689d6a +regular7=a89984 + +# Bright ANSI (8-15) +bright0=928374 +bright1=fb4934 +bright2=b8bb26 +bright3=fabd2f +bright4=83a598 +bright5=d3869b +bright6=8ec07c +bright7=ebdbb2 + +# Selection (FG BG pair) +selection-foreground=1d2021 +selection-background=ebdbb2 +# Search box (FG BG pairs) +search-box-match=ebdbb2 458588 +search-box-no-match=ebdbb2 cc241d + +[bell] +urgent=no +notify=no +visual=no + +[url] +launch=xdg-open {url} diff --git a/dot_config/foot/patch.py b/dot_config/foot/patch.py new file mode 100644 index 0000000..dd53e37 --- /dev/null +++ b/dot_config/foot/patch.py @@ -0,0 +1,25 @@ +from pathlib import Path +p = Path("/home/rain/.config/foot/foot.ini") +text = p.read_text() +old_lines = [ + "# foot does not expand env variables in the `shell=` value, so we have to", + "# go through `sh -c` to make the parent's $SHELL take effect. When foot", + "# is launched from a fish session, $SHELL is /bin/fish, and foot runs fish.", + "# When launched from bash, $SHELL is /bin/bash, and foot runs bash. The", + "# default fallback is /bin/bash if $SHELL is unset for any reason.", + 'shell=sh,-c,"exec ${SHELL:-/bin/bash} -i"', +] +new_lines = [ + "# kaiser runs zsh as the login shell. Hardcode /usr/bin/zsh here.", + "# foot does NOT expand $SHELL in shell= (it passes the literal string", + "# to its child process), so the sh,-c trick makes foot try to exec a", + '# binary literally named "${SHELL:-/bin/bash}" and die with', + '# "No such file or directory". Point shell= at the binary directly.', + "shell=/usr/bin/zsh", +] +old = "\n".join(old_lines) +new = "\n".join(new_lines) +assert old in text, "old block not found in foot.ini" +text = text.replace(old, new, 1) +p.write_text(text) +print("OK, new size:", len(text)) diff --git a/dot_config/mako/config b/dot_config/mako/config new file mode 100644 index 0000000..3abb9e5 --- /dev/null +++ b/dot_config/mako/config @@ -0,0 +1,22 @@ +# mako gruvbox-material-dark-hard +font=Maple Mono NF 11 +background-color=#1d2021 +text-color=#ebdbb2 +border-size=2 +border-color=#d79921 +border-radius=0 +default-timeout=5000 +padding=10 +margin=8 +width=400 + +[urgency=high] +border-color=#fb4934 +default-timeout=0 + +[urgency=normal] +border-color=#d79921 + +[urgency=low] +border-color=#504945 +default-timeout=3000 diff --git a/dot_config/sway/caffeine.sh b/dot_config/sway/caffeine.sh new file mode 100755 index 0000000..bf68488 --- /dev/null +++ b/dot_config/sway/caffeine.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# caffeine.sh — toggle caffeine mode by killing/restarting swayidle +# When ON: kill swayidle (no idle lock/screen-off) +# When OFF: restart swayidle with normal timeouts + +WALLPAPER="$HOME/.local/share/wallpapers/teach-invaders-gruvbox.png" +FLAG="/tmp/caffeine-inhibit" + +if [ -f "$FLAG" ]; then + # Turn OFF caffeine — restart swayidle + rm -f "$FLAG" + killall swayidle 2>/dev/null + swayidle -w \ + timeout 300 "swaylock -f -i $WALLPAPER" \ + timeout 600 'swaymsg "output * power off"' \ + resume 'swaymsg "output * power on"' \ + before-sleep "swaylock -f -i $WALLPAPER" & + notify-send -t 2000 "☕ Caffeine OFF" "Idle sleep enabled" 2>/dev/null +else + # Turn ON caffeine — kill swayidle + touch "$FLAG" + killall swayidle 2>/dev/null + notify-send -t 2000 "☕ Caffeine ON" "Idle sleep disabled" 2>/dev/null +fi diff --git a/dot_config/sway/cheatsheet.md b/dot_config/sway/cheatsheet.md new file mode 100644 index 0000000..11c1191 --- /dev/null +++ b/dot_config/sway/cheatsheet.md @@ -0,0 +1,48 @@ +# === sway keybinds cheatsheet === +# generated from ~/.config/sway/config — `Super` = the Windows key (Mod4) +# reload this view with: Super + / + +# LAUNCHERS +Super+Enter open foot terminal +Super+D open wofi app launcher +Super+E open thunar (file manager) +Super+B open firefox +Super+/ show this cheatsheet (in foot, via less) +Super+Shift+E exit sway (with prompt) + +# WINDOW MANAGEMENT +Super+Q kill focused window +Super+F fullscreen toggle +Super+V split toggle (vertical <-> horizontal) +Super+S stacking layout +Super+W tabbed layout +Super+Space floating toggle + +# FOCUS / MOVE +Super+arrows focus window in direction +Super+Shift+arrows move window in direction + +# WORKSPACES +Super+1 .. Super+0 switch to workspace N (0 = 10) +Super+Tab toggle back to previous workspace +Super+Shift+1..0 move focused window to workspace N + +# SCREENSHOTS +Print region screenshot to file +Super+Print full-screen screenshot to file +Super+Shift+S region screenshot to clipboard + +# SCRATCHPAD +Super+` toggle dropdown foot (sway scratchpad) + +# RESIZE MODE (Super+R enters, Esc/Enter exits) +resize mode: + arrows shrink/grow 30px in direction + Esc / Enter exit resize mode + +# LOCK / IDLE +Ctrl+Alt+L lock now +5 min idle auto-lock +10 min idle auto screen-off + +# TIP: Super+Shift+C reloads this config without logging out. diff --git a/dot_config/sway/cheatsheet.sh b/dot_config/sway/cheatsheet.sh new file mode 100755 index 0000000..24c516c --- /dev/null +++ b/dot_config/sway/cheatsheet.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# cheatsheet.sh — opens the sway keybind cheatsheet in a new foot terminal +# running less. Press q to quit. +exec foot sh -c 'exec less -SRFX "$HOME/.config/sway/cheatsheet.md"' diff --git a/dot_config/sway/config b/dot_config/sway/config new file mode 100644 index 0000000..047b750 --- /dev/null +++ b/dot_config/sway/config @@ -0,0 +1,148 @@ +# === ~/.config/sway/config === +# MINIMAL — single file, no includes. Tier-1+2+3 in one go. +# We can split into includes later once this is proven to boot. + +# === variables === +set $mod Mod4 +set $term foot +set $menu wofi --show drun --insensitive -p "launch" +set $browser zen +set $fileManager thunar + +# === output / input === +# Gruvbox wallpaper — sway's native bg command manages swaybg lifecycle internally +output * bg $HOME/.local/share/wallpapers/teach-invaders-gruvbox.png fill + +# Touchpad: tap-to-click, natural scroll, drag +input type:touchpad { + tap enabled + tap_button_map lrm + natural_scroll enabled + dwt enabled + accel_profile adaptive +} + +# === window borders (gruvbox-hard) === +default_border pixel 2 +default_floating_border pixel 2 +gaps inner 8 +gaps outer 4 +smart_borders on +smart_gaps on + +# gruvbox-orange focus border (replaces the default "generic blue") +client.focused #d79921 #1d2021 #ebdbb2 #d79921 #d79921 +client.unfocused #3c3836 #1d2021 #a89984 #3c3836 #3c3836 +client.focused_inactive #504945 #1d2021 #a89984 #504945 #504945 +client.urgent #fb4934 #1d2021 #ebdbb2 #fb4934 #fb4934 + +# === swaybar (replaced by waybar — keeping as fallback) === +# If waybar isn't installed yet, this provides a basic bar. +bar { + swaybar_command waybar +} + +# === autostart (kept tiny, all stderr silenced) === +# Make user-local themes/icons visible to GTK and Qt +exec sh -c 'export XDG_DATA_DIRS="$HOME/.local/share:$HOME/.local/share/flatpak/exports/share:/usr/local/share:/usr/share"; export XDG_DATA_HOME="$HOME/.local/share"; export XDG_CONFIG_HOME="$HOME/.config"; systemctl --user import-environment XDG_DATA_DIRS XDG_DATA_HOME XDG_CONFIG_HOME 2>/dev/null; dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway' +exec /usr/libexec/polkit-gnome-authentication-agent-1 +exec clipmenud +exec sh -c 'systemctl --user start xdg-desktop-portal xdg-desktop-portal-wlr 2>/dev/null || true' +exec sh -c 'gsettings set org.gnome.desktop.interface gtk-theme Gruvbox-Material-Dark-Hard 2>/dev/null; gsettings set org.gnome.desktop.interface icon-theme Gruvbox-Material-Dark 2>/dev/null; gsettings set org.gnome.desktop.interface color-scheme prefer-dark 2>/dev/null; true' +exec gnome-keyring-daemon --start --components=secrets +exec mako +exec wlsunset -l 39.96 -L -82.99 +exec sworkstyle + +# === idle / lock === +exec swayidle -w \ + timeout 300 '$HOME/.config/sway/lock-fancy.sh' \ + timeout 600 'swaymsg "output * power off"' \ + resume 'swaymsg "output * power on"' \ + before-sleep '$HOME/.config/sway/lock-fancy.sh' + +# === keybinds === +bindsym $mod+Return exec $term +bindsym $mod+d exec $menu +bindsym $mod+e exec $fileManager +bindsym $mod+b exec $browser +bindsym $mod+q kill +bindsym $mod+Shift+c reload +bindsym Ctrl+Alt+l exec $HOME/.config/sway/lock-fancy.sh +bindsym $mod+slash exec $HOME/.config/sway/cheatsheet.sh +bindsym $mod+question exec $HOME/.config/sway/cheatsheet.sh +bindsym $mod+Shift+e exec swaynag -t warning -m 'Exit Sway?' -B 'Yes, exit Sway' 'swaymsg exit' + +bindsym $mod+Left focus left +bindsym $mod+Down focus down +bindsym $mod+Up focus up +bindsym $mod+Right focus right +bindsym $mod+Shift+Left move left +bindsym $mod+Shift+Down move down +bindsym $mod+Shift+Up move up +bindsym $mod+Shift+Right move right + +bindsym $mod+1 workspace number 1 +bindsym $mod+2 workspace number 2 +bindsym $mod+3 workspace number 3 +bindsym $mod+4 workspace number 4 +bindsym $mod+5 workspace number 5 +bindsym $mod+6 workspace number 6 +bindsym $mod+7 workspace number 7 +bindsym $mod+8 workspace number 8 +bindsym $mod+9 workspace number 9 +bindsym $mod+0 workspace number 10 +bindsym $mod+Tab workspace back_and_forth +bindsym $mod+Shift+1 move container to workspace number 1 +bindsym $mod+Shift+2 move container to workspace number 2 +bindsym $mod+Shift+3 move container to workspace number 3 +bindsym $mod+Shift+4 move container to workspace number 4 +bindsym $mod+Shift+5 move container to workspace number 5 +bindsym $mod+Shift+6 move container to workspace number 6 +bindsym $mod+Shift+7 move container to workspace number 7 +bindsym $mod+Shift+8 move container to workspace number 8 +bindsym $mod+Shift+9 move container to workspace number 9 +bindsym $mod+Shift+0 move container to workspace number 10 + +bindsym $mod+f fullscreen +bindsym $mod+v split toggle splitv +bindsym $mod+s layout stacking +bindsym $mod+w layout tabbed +bindsym $mod+space exec $menu +bindsym $mod+Shift+space floating toggle + +bindsym Print exec grim -g "$(slurp)" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png +bindsym $mod+Print exec grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png +bindsym $mod+Shift+s exec grim -g "$(slurp)" - | wl-copy + +mode "resize" { + bindsym Left resize shrink width 30px + bindsym Down resize grow height 30px + bindsym Up resize shrink height 30px + bindsym Right resize grow width 30px + bindsym Escape mode "default" + bindsym Return mode "default" +} +bindsym $mod+r mode "resize" + +# === F12 dropdown terminal === +# F12 opens a floating foot. Uses -a dropdown so for_window only floats +# the dropdown, not regular foot windows from Super+Enter. +# Close it with Super+Q or Ctrl+D. +bindsym F12 exec foot -a dropdown +bindsym $mod+grave exec foot -a dropdown +for_window [app_id="dropdown"] floating enable, sticky enable, resize set width 1100 height 600, move position center + +# === media keys === +bindsym --locked XF86AudioRaiseVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+ +bindsym --locked XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- +bindsym --locked XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle +bindsym --locked XF86AudioMicMute exec wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle +bindsym --locked XF86AudioPlay exec playerctl play-pause 2>/dev/null || true +bindsym --locked XF86AudioNext exec playerctl next 2>/dev/null || true +bindsym --locked XF86AudioPrev exec playerctl previous 2>/dev/null || true +bindsym --locked XF86MonBrightnessUp exec sh -c 'echo $(( $(cat /sys/class/backlight/acpi_video0/brightness) + 1 > $(cat /sys/class/backlight/acpi_video0/max_brightness) ? $(cat /sys/class/backlight/acpi_video0/max_brightness) : $(cat /sys/class/backlight/acpi_video0/brightness) + 1 )) | sudo tee /sys/class/backlight/acpi_video0/brightness' +bindsym --locked XF86MonBrightnessDown exec sh -c 'echo $(( $(cat /sys/class/backlight/acpi_video0/brightness) - 1 < 0 ? 0 : $(cat /sys/class/backlight/acpi_video0/brightness) - 1 )) | sudo tee /sys/class/backlight/acpi_video0/brightness' + +# pointer / mouse — the gruvbox-material-gtk repo doesn't ship an xcursor theme, +# so leave the default alone. (Cursor shows fine on the default theme.) diff --git a/dot_config/sway/config.d/00-gruvbox.conf b/dot_config/sway/config.d/00-gruvbox.conf new file mode 100644 index 0000000..5d90acd --- /dev/null +++ b/dot_config/sway/config.d/00-gruvbox.conf @@ -0,0 +1,18 @@ +# === ~/.config/sway/config.d/00-gruvbox.conf === +# gruvbox material dark hard — wired through gsettings so Thunar, Firefox chrome, +# and any GTK3/4 app pick it up the moment sway starts. + +# Run gsettings in a sh -c wrapper so we can pipe stderr to /dev/null. +# This is necessary because the very first exec block runs before dbus is +# always reachable, and gsettings prints a long bar-warning to stderr when +# it can't find the bus. We don't want the red bar to surface that. +exec_always { + gsettings set org.gnome.desktop.interface gtk-theme 'Gruvbox-Material-Dark-Hard' 2>/dev/null + gsettings set org.gnome.desktop.interface icon-theme 'Gruvbox-Material-Dark' 2>/dev/null + gsettings set org.gnome.desktop.interface cursor-theme 'Gruvbox-Material-Dark' 2>/dev/null + gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' 2>/dev/null + gsettings set org.cinnamon.desktop.interface gtk-theme 'Gruvbox-Material-Dark-Hard' 2>/dev/null +} + +# gnome-keyring (in case any GTK app needs secret service for ssh-agent bridge) +exec gnome-keyring-daemon --start --components=secrets diff --git a/dot_config/sway/config.d/10-keybinds.conf b/dot_config/sway/config.d/10-keybinds.conf new file mode 100644 index 0000000..094d984 --- /dev/null +++ b/dot_config/sway/config.d/10-keybinds.conf @@ -0,0 +1,76 @@ +# === ~/.config/sway/config.d/10-keybinds.conf === +# sway keybindings. Mint+Cinnamon muscle memory → Super (Mod4). + +# === essentials === +bindsym $mod+Return exec $term +bindsym $mod+d exec $menu +bindsym $mod+e exec $fileManager +bindsym $mod+b exec $browser +bindsym $mod+q kill +bindsym $mod+Shift+c reload +bindsym $mod+Shift+e exec swaynag -t warning \ + -m 'Exit Sway?' \ + -B 'Yes, exit Sway' 'swaymsg exit' + +# === move focus === +bindsym $mod+Left focus left +bindsym $mod+Down focus down +bindsym $mod+Up focus up +bindsym $mod+Right focus right +bindsym $mod+Shift+Left move left +bindsym $mod+Shift+Down move down +bindsym $mod+Shift+Up move up +bindsym $mod+Shift+Right move right + +# === workspace navigation === +bindsym $mod+1 workspace number 1 +bindsym $mod+2 workspace number 2 +bindsym $mod+3 workspace number 3 +bindsym $mod+4 workspace number 4 +bindsym $mod+5 workspace number 5 +bindsym $mod+6 workspace number 6 +bindsym $mod+7 workspace number 7 +bindsym $mod+8 workspace number 8 +bindsym $mod+9 workspace number 9 +bindsym $mod+0 workspace number 10 +bindsym $mod+Tab workspace back_and_forth +bindsym $mod+Shift+1 move container to workspace number 1 +bindsym $mod+Shift+2 move container to workspace number 2 +bindsym $mod+Shift+3 move container to workspace number 3 +bindsym $mod+Shift+4 move container to workspace number 4 +bindsym $mod+Shift+5 move container to workspace number 5 +bindsym $mod+Shift+6 move container to workspace number 6 +bindsym $mod+Shift+7 move container to workspace number 7 +bindsym $mod+Shift+8 move container to workspace number 8 +bindsym $mod+Shift+9 move container to workspace number 9 +bindsym $mod+Shift+0 move container to workspace number 10 + +# === layout === +bindsym $mod+f fullscreen +bindsym $mod+v split toggle splitv +bindsym $mod+s layout stacking +bindsym $mod+w layout tabbed +bindsym $mod+space floating toggle +bindsym $mod+Shift+space focus mode_toggle + +# === screenshot (grim + slurp) === +bindsym Print exec grim -g "$(slurp)" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png +bindsym $mod+Print exec grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png +bindsym $mod+Shift+s exec grim -g "$(slurp)" - | wl-copy + +# === resize === +mode "resize" { + bindsym Left resize shrink width 30px + bindsym Down resize grow height 30px + bindsym Up resize shrink height 30px + bindsym Right resize grow width 30px + bindsym Escape mode "default" + bindsym Return mode "default" +} +bindsym $mod+r mode "resize" + +# === scratchpads (quick terminal / browser) === +# Start a headless foot server, then a dropdown client window. Toggle with $mod+grave. +bindsym $mod+grave exec foot --server +bindsym $mod+apostrophe exec foot -a dropdown +for_window [app_id="dropdown"] floating enable, sticky enable, resize set width 1100 height 600, move position center diff --git a/dot_config/sway/config.d/20-autostart.conf b/dot_config/sway/config.d/20-autostart.conf new file mode 100644 index 0000000..1952cff --- /dev/null +++ b/dot_config/sway/config.d/20-autostart.conf @@ -0,0 +1,15 @@ +# === ~/.config/sway/config.d/20-autostart.conf === +# background services started when sway comes up. + +# polkit agent — needed for gparted, mount UIs in thunar, etc. +exec /usr/libexec/polkit-gnome-authentication-agent-1 + +# clipboard — start clipmenud directly (it watches the clipboard internally) +# Drop the wl-paste --watch wrapper to avoid bar noise if the socket isn't ready. +exec clipmenud + +# xdg-desktop-portal — required for native file/screen sharing in Firefox/Chromium. +# Skip if services aren't enabled; the user can enable with: +# systemctl --user enable --now xdg-desktop-portal xdg-desktop-portal-wlr +# Until then, those systemctl lines are quiet no-ops. +exec sh -c 'systemctl --user start xdg-desktop-portal xdg-desktop-portal-wlr 2>/dev/null || true' diff --git a/dot_config/sway/config.d/50-launchers.conf b/dot_config/sway/config.d/50-launchers.conf new file mode 100644 index 0000000..159d7f9 --- /dev/null +++ b/dot_config/sway/config.d/50-launchers.conf @@ -0,0 +1,17 @@ +# ~/.config/sway/config.d/50-launchers.conf +# Launcher keybinds. The base $menu var in config is the drun launcher. +# These add run mode, window switcher, and the powermenu. + +# $mod+grave already used by foot dropdown — don't clobber. +# $mod+Tab already used by workspace back_and_forth — don't clobber. +# $mod+slash is the cheatsheet — keep it. + +# Exec ad-hoc command (not in $PATH discovery) +bindsym $mod+F12 exec wofi --show run --prompt "exec" + +# Sway window switcher (replaces `swayr`-style overview) +bindsym $mod+F11 exec wofi --show window + +# Power menu — dmenu mode driven by wofi-powermenu script. +# wofi has no custom-mode feature; the script does the dispatch. +bindsym $mod+Escape exec $HOME/.local/bin/wofi-powermenu diff --git a/dot_config/sway/lock-fancy.sh b/dot_config/sway/lock-fancy.sh new file mode 100755 index 0000000..932b9bc --- /dev/null +++ b/dot_config/sway/lock-fancy.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# lock-fancy.sh — fancy swaylock: snapshot the screen, dim+blur it, and use as background. +# grim + ffmpeg (both already installed). No swaylock-effects overlay needed. + +set -e + +TMP=/tmp/lock-bg-483833.png +DIM=/tmp/lock-bg-dim-483833.png + +# Snapshot the current screen +grim -t png "$TMP" 2>/dev/null + +# Dim + blur via ffmpeg (much faster than ImageMagick) +if [ -f "$TMP" ] && command -v ffmpeg >/dev/null; then + ffmpeg -loglevel error -y -i "$TMP" -vf "boxblur=12:1,eq=brightness=-0.30:saturation=0.6" -frames:v 1 "$DIM" + IMG="$DIM" +else + # Fallback to the regular wallpaper + IMG="$HOME/.local/share/wallpapers/teach-invaders-gruvbox.png" +fi + +# Launch swaylock +swaylock -f -i "$IMG" \ + --indicator-radius 100 \ + --indicator-thickness 6 \ + --ring-color ebdbb2 \ + --inside-color 1d202180 \ + --key-hl-color fe8019 \ + --bs-hl-color fb4934 \ + --text-color ebdbb2 \ + --separator-color 00000000 \ + --fade-in 0.3 + +rm -f "$TMP" "$DIM" 2>/dev/null diff --git a/dot_config/sway/toggle-dropdown.sh b/dot_config/sway/toggle-dropdown.sh new file mode 100755 index 0000000..ac63c9b --- /dev/null +++ b/dot_config/sway/toggle-dropdown.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# toggle-dropdown.sh — toggle the dropdown foot terminal +# Writes debug info to /tmp/sway-f12.log so we can diagnose from SSH + +LOG=/tmp/sway-f12.log +echo "=== $(date) F12/Super+grave pressed ===" >> "$LOG" +echo "WAYLAND_DISPLAY=$WAYLAND_DISPLAY" >> "$LOG" +echo "USER=$USER XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >> "$LOG" + +# Find the sway socket +SOCK=$(ls -t /run/user/$(id -u)/sway-ipc.*.sock 2>/dev/null | head -1) +echo "SOCK=$SOCK" >> "$LOG" + +if [ -z "$SOCK" ]; then + echo "ERROR: no sway socket found" >> "$LOG" + notify-send -t 2000 "sway: no IPC socket" 2>>"$LOG" || true + exit 1 +fi + +# Check if a foot window is in the scratchpad (hidden) +HIDDEN=$(SWAYSOCK=$SOCK swaymsg -t get_tree 2>>"$LOG" | python3 -c " +import sys, json +try: + tree = json.load(sys.stdin) + def find_app(node, app_id): + if node.get('app_id') == app_id and node.get('name', '').startswith('__i3'): + return node + for c in node.get('nodes', []) + node.get('floating_nodes', []): + r = find_app(c, app_id) + if r is not None: + return r + return None + dd = find_app(tree, 'foot') + print('hidden' if dd is not None else 'none') +except Exception as e: + print(f'err:{e}') +" 2>>"$LOG") + +echo "HIDDEN=$HIDDEN" >> "$LOG" + +if [ "$HIDDEN" = "hidden" ]; then + SWAYSOCK=$SOCK swaymsg '[app_id="foot"] scratchpad show' >>"$LOG" 2>&1 + echo "toggled: scratchpad show" >> "$LOG" +else + # Spawn a new foot. Pass through WAYLAND_DISPLAY explicitly. + if [ -n "$WAYLAND_DISPLAY" ]; then + WAYLAND_DISPLAY=$WAYLAND_DISPLAY foot >>"$LOG" 2>&1 & + else + # try to find wayland-1 socket + if [ -S "/run/user/$(id -u)/wayland-1" ]; then + WAYLAND_DISPLAY=wayland-1 foot >>"$LOG" 2>&1 & + else + foot >>"$LOG" 2>&1 & + fi + fi + echo "spawned foot" >> "$LOG" +fi +echo "=== done ===" >> "$LOG" diff --git a/dot_config/sway/wifi-menu.sh b/dot_config/sway/wifi-menu.sh new file mode 100755 index 0000000..f47a54c --- /dev/null +++ b/dot_config/sway/wifi-menu.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# wifi-menu.sh — wofi-based WiFi network picker using nmcli +# Lists available networks, connects on selection. + +# Get list of networks: SSID, signal, security +SELECTED=$(nmcli -t -f SSID,SIGNAL,SECURITY device wifi list --rescan yes 2>/dev/null | \ + grep -v '^$' | \ + sort -t: -k2 -nr | \ + awk -F: '{ + sig = $2 + 0 + if (sig > 75) bars = "████" + else if (sig > 50) bars = "███░" + else if (sig > 25) bars = "██░░" + else bars = "█░░░" + sec = ($3 != "" && $3 != "--") ? " 󰌾" : "" + printf "%s %s%s\n", bars, $1, sec + }' | \ + wofi --dmenu --insensitive --prompt "WiFi" -W 400 -H 500 2>/dev/null) + +# Exit if nothing selected +[ -z "$SELECTED" ] && exit 0 + +# Extract SSID (everything after the signal bars + spaces) +SSID=$(echo "$SELECTED" | sed 's/^[█░ ]* //; s/ 🔒$//') + +# Connect +nmcli device wifi connect "$SSID" 2>&1 | grep -q "successfully" && \ + notify-send -t 3000 "WiFi" "Connected to $SSID" || \ + notify-send -t 5000 "WiFi" "Failed to connect to $SSID" diff --git a/dot_config/waybar/config b/dot_config/waybar/config new file mode 100644 index 0000000..e6109f9 --- /dev/null +++ b/dot_config/waybar/config @@ -0,0 +1,102 @@ +{ + "layer": "top", + "position": "top", + "height": 32, + "spacing": 0, + "modules-left": [ + "sway/workspaces", + "sway/mode" + ], + "modules-center": [ + "clock", + "custom/caffeine" + ], + "modules-right": [ + "pulseaudio", + "battery", + "cpu", + "memory", + "tray", + "network" + ], + "sway/workspaces": { + "disable-scroll": true, + "format": "{name}", + "format-icons": { + "focused": "\u25cf", + "default": "\u25cb" + } + }, + "clock": { + "format": " {:%a %b %d %H:%M}", + "tooltip-format": "{:%Y %B}\n{calendar}", + "format-alt": " {:%Y-%m-%d}" + }, + "pulseaudio": { + "format": "{icon} {volume}%", + "format-muted": "\udb81\udd81 muted", + "format-icons": { + "default": [ + "\udb81\udd7f", + "\udb81\udd80", + "\udb81\udd7e" + ] + }, + "scroll-step": 5, + "on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", + "on-click-right": "pavucontrol" + }, + "battery": { + "states": { + "good": 80, + "warning": 30, + "critical": 15 + }, + "format": "{icon} {capacity}%", + "format-charging": "\udb80\udc84 {capacity}%", + "format-plugged": "\udb80\udc84 {capacity}%", + "format-icons": [ + "\udb80\udc8e", + "\udb80\udc7a", + "\udb80\udc7b", + "\udb80\udc7c", + "\udb80\udc7d", + "\udb80\udc7e", + "\udb80\udc7f", + "\udb80\udc80", + "\udb80\udc81", + "\udb80\udc82", + "\udb80\udc79" + ] + }, + "cpu": { + "format": "\udb83\udee0 {usage}%", + "tooltip": true + }, + "memory": { + "format": "\udb80\udf5b {}%", + "tooltip-format": "{used:0.1f}G / {total:0.1f}G" + }, + "network": { + "format-wifi": "\udb82\udd28 {essid}", + "format-ethernet": "\udb80\ude00 {ipaddr}", + "format-disconnected": "\udb82\udd2d disconnected", + "format-linked": "\udb82\udd2a {ifname}", + "tooltip-format": "{ifname} \u2014 {ipaddr}", + "on-click": "$HOME/.config/sway/wifi-menu.sh" + }, + "custom/caffeine": { + "format": "{icon}", + "format-icons": { + "default": "\udb80\udd76", + "active": "\u2615" + }, + "exec": "if [ -f /tmp/caffeine-inhibit ]; then echo '{\"alt\":\"active\",\"text\":\"caffeine\"}'; else echo '{\"alt\":\"default\",\"text\":\"caffeine\"}'; fi", + "exec-interval": 2, + "return-type": "json", + "on-click": "$HOME/.config/sway/caffeine.sh" + }, + "tray": { + "spacing": 8 + } +} diff --git a/dot_config/waybar/style.css b/dot_config/waybar/style.css new file mode 100644 index 0000000..3bc6de5 --- /dev/null +++ b/dot_config/waybar/style.css @@ -0,0 +1,92 @@ +/* === waybar — gruvbox-material-dark-hard, polished === */ + +* { + font-family: "Maple Mono NF"; + font-size: 14px; + min-height: 0; + padding: 0; + margin: 0; + border: none; +} + +/* transparent bar so the desktop bleeds through at the edges */ +window#waybar { + background-color: rgba(29, 32, 33, 0.92); + color: #ebdbb2; + border-bottom: 1px solid #3c3836; +} + +window#waybar.hidden { opacity: 0.2; } + +/* workspaces */ +#workspaces button { + padding: 0 12px; + background: transparent; + color: #a89984; + box-shadow: inset 0 -2px transparent; + transition: all 200ms ease; +} +#workspaces button:hover { + background: #3c3836; + color: #ebdbb2; +} +#workspaces button.focused { + color: #fabd2f; + box-shadow: inset 0 -2px #fabd2f; +} +#workspaces button.urgent { + color: #fb4934; + animation: blink 1s linear infinite alternate; +} + +#mode { + color: #fb4934; + background: #282828; + padding: 0 12px; + font-weight: bold; +} + +#clock { + color: #ebdbb2; + font-weight: bold; + padding: 0 16px; + border-left: 1px solid #3c3836; + border-right: 1px solid #3c3836; +} +#clock:hover { color: #fabd2f; } + +/* right-side modules — shared layout, individual color */ +#pulseaudio, #battery, #cpu, #memory, #network, #tray, #custom-caffeine { + padding: 0 12px; + border-left: 1px solid #3c3836; + transition: all 200ms ease; +} +#pulseaudio:hover, #battery:hover, #cpu:hover, #memory:hover, #network:hover, #tray:hover, #custom-caffeine:hover { + background: #3c3836; +} + +#pulseaudio { color: #b8bb26; } +#pulseaudio.muted { color: #928374; } +#battery { color: #d79921; } +#battery.charging { color: #b8bb26; } +#battery.good { color: #b8bb26; } +#battery.warning { color: #fe8019; } +#battery.critical { + color: #fb4934; + animation: blink 1s linear infinite alternate; +} +#cpu { color: #83a598; } +#memory { color: #d3869b; } +#network { color: #fabd2f; } +#network.disconnected { color: #fb4934; } +#tray { padding: 0 10px; } + +#custom-caffeine { color: #a89984; } +#custom-caffeine.active { color: #fabd2f; } +#custom-caffeine.active:hover { background: #3c3836; } + +#sway-workspaces { margin: 0 4px; } + +@keyframes blink { + to { opacity: 0.3; } +} diff --git a/dot_config/wofi/config b/dot_config/wofi/config new file mode 100644 index 0000000..3c54b9e --- /dev/null +++ b/dot_config/wofi/config @@ -0,0 +1,19 @@ +# ~/.config/wofi/config — gruvbox launcher defaults +# Loaded by wofi before style.css. CLI flags still win. + +[window] +location=top +width=620 +height=420 + +[prompt] +prompt=go +insensitive=true + +[search] +matching=fuzzy +sort_order=default + +[keybindings] +return=activate +escape=close diff --git a/dot_config/wofi/powermenu.config b/dot_config/wofi/powermenu.config new file mode 100644 index 0000000..07cb4eb --- /dev/null +++ b/dot_config/wofi/powermenu.config @@ -0,0 +1,12 @@ +# ~/.config/wofi/powermenu.config — plain INI, not rasi +# wofi's config format is INI. There is no rasi-style `configuration { }` +# block. The icon theme comes from gsettings / dconf, not from here. + +location=top +yoffset=32 +width=420 +height=280 +prompt=power +insensitive=true +hide_scroll=true +matching=exact diff --git a/dot_config/wofi/style.css b/dot_config/wofi/style.css new file mode 100644 index 0000000..c652fcd --- /dev/null +++ b/dot_config/wofi/style.css @@ -0,0 +1,57 @@ +/* wofi gruvbox-material-dark-hard — extended rice */ +window { + margin: 0px; + border: 2px solid #d79921; + border-radius: 10px; + background-color: #1d2021; + background-image: linear-gradient(to bottom, #1d2021, #282828); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); + font-family: "Maple Mono NF"; + font-size: 14px; +} + +#input { + margin: 0px; + padding: 10px; + border: none; + border-radius: 10px 10px 0 0; + color: #ebdbb2; + background-color: #282828; +} + +#inner-box { + margin: 0px; + border: none; + background-color: transparent; +} + +#outer-box { + margin: 0px; + border: none; + background-color: transparent; +} + +#scroll { + margin: 0px; + border: none; +} + +#text { + margin: 0px; + padding: 6px 10px; + border: none; + color: #ebdbb2; + background-color: transparent; +} + +#entry { + transition: background-color 120ms ease-out; +} + +#entry:selected { + background-color: #3c3836; +} + +#entry:selected #text { + color: #fabd2f; +} diff --git a/run_once_40-install-sway.sh.tmpl b/run_once_40-install-sway.sh.tmpl new file mode 100644 index 0000000..e3000e4 --- /dev/null +++ b/run_once_40-install-sway.sh.tmpl @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# ============================================================================= +# run_once_40-install-sway.sh.tmpl +# Install the sway + wofi + foot + supporting tooling desktop stack. +# +# Gated on .sway (set in .chezmoi.yaml.tmpl). When false (Pis, headless +# boxes, etc.), this script exits 0 without doing anything. +# +# To opt a new box in: `touch ~/.config/chezmoi/features/sway` then +# `chezmoi apply`. To opt out: `touch ~/.config/chezmoi/features/no-sway`. +# +# Idempotent: skips if sway is already installed. +# ============================================================================= +set -euo pipefail + +log() { printf '\033[1;34m[sway]\033[0m %s\n' "$*"; } + +# --- 0. Gate on the .sway flag --- +{{ if not .sway -}} +log "sway not enabled for this host (.sway=false). Skipping." +log "To enable: touch ~/.config/chezmoi/features/sway && chezmoi apply" +exit 0 +{{ end -}} + +# --- 1. Install packages --- +SWAY_PKGS=(sway wofi foot swaybg swaylock swayidle grim slurp waybar wl-clipboard) + +{{ if eq .os_family "arch" -}} +# mako is in arch [extra] +SWAY_PKGS+=(mako) +{{ else if eq .os_family "debian" -}} +# mako isn't packaged on debian. Use dunst (lightweight notification daemon, +# widely compatible with sway/waybar setups). +SWAY_PKGS+=(dunst) +{{ else -}} +log "WARNING: sway packages not configured for os_family={{ .os_family }}" +{{ end -}} + +log "installing sway stack: ${SWAY_PKGS[*]}" + +{{ if eq .os_family "arch" -}} +sudo pacman -S --needed --noconfirm "${SWAY_PKGS[@]}" +{{ else if eq .os_family "debian" -}} +export DEBIAN_FRONTEND=noninteractive +sudo apt-get install -y --no-install-recommends "${SWAY_PKGS[@]}" +{{ end -}} + +log "sway stack installed" +sway --version 2>&1 | head -1 +foot --version 2>&1 | head -1 +wofi --version 2>&1 | head -1 + +# --- 2. Mark the box so subsequent chezmoi applies know sway is enabled --- +mkdir -p ~/.config/chezmoi/features +touch ~/.config/chezmoi/features/sway +log "marked box: ~/.config/chezmoi/features/sway" + +log "sway stack install complete" \ No newline at end of file