summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/.stowrc1
-rw-r--r--arch/eww/.config/eww/eww.scss112
-rw-r--r--arch/eww/.config/eww/eww.yuck150
-rwxr-xr-xarch/eww/.config/eww/scripts/active-workspace4
-rwxr-xr-xarch/eww/.config/eww/scripts/active-ws2
-rwxr-xr-xarch/eww/.config/eww/scripts/cpu7
-rwxr-xr-xarch/eww/.config/eww/scripts/hypr-socket24
-rwxr-xr-xarch/eww/.config/eww/scripts/netspeed9
-rwxr-xr-xarch/eww/.config/eww/scripts/powermenu7
-rwxr-xr-xarch/eww/.config/eww/scripts/workspaces6
-rw-r--r--arch/fish/.config/fish/conf.d/aliases.fish17
-rw-r--r--arch/fish/.config/fish/conf.d/edit-configs.fish12
-rw-r--r--arch/fish/.config/fish/config.fish25
-rw-r--r--arch/fish/.config/fish/fish_variables4
-rw-r--r--arch/ghostty/.config/ghostty/config21
-rw-r--r--arch/git/.config/git/config21
-rw-r--r--arch/hyprland/.config/hypr/hypridle.conf32
-rw-r--r--arch/hyprland/.config/hypr/hyprland.conf86
-rw-r--r--arch/hyprland/.config/hypr/hyprlock.conf52
-rw-r--r--arch/hyprland/.config/hypr/keybinds.conf106
-rw-r--r--arch/hyprland/.config/hypr/monitors.conf8
-rw-r--r--arch/hyprland/.config/hypr/theme.conf7
-rw-r--r--arch/hyprland/.config/hypr/windowrules.conf62
-rwxr-xr-xarch/install.sh191
-rw-r--r--arch/mako/.config/mako/config15
-rw-r--r--arch/nvim/.config/nvim/.stylua.toml3
-rw-r--r--arch/nvim/.config/nvim/init.lua27
-rw-r--r--arch/nvim/.config/nvim/lazy-lock.json30
-rw-r--r--arch/nvim/.config/nvim/lua/keymaps.lua41
-rw-r--r--arch/nvim/.config/nvim/lua/options.lua39
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/colorscheme.lua74
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/completion.lua40
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/editor.lua73
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/extras.lua37
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/formatting.lua47
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/fzf.lua23
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/linting.lua17
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/lsp.lua75
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/nav.lua16
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/treesitter.lua34
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/ui.lua105
-rw-r--r--arch/nvim/.config/nvim/lua/plugins/whichkey.lua25
-rwxr-xr-xarch/scripts/.local/bin/launcher26
-rwxr-xr-xarch/scripts/.local/bin/pick-wallpaper11
-rwxr-xr-xarch/scripts/.local/bin/set-wallpaper27
-rw-r--r--arch/spicetify/.config/spicetify/Themes/wallust/color.ini19
-rw-r--r--arch/spicetify/.config/spicetify/Themes/wallust/user.css72
m---------arch/tmux/.config/tmux/plugins/tmux-resurrect0
m---------arch/tmux/.config/tmux/plugins/tmux-sensible0
m---------arch/tmux/.config/tmux/plugins/tmux-yank0
m---------arch/tmux/.config/tmux/plugins/tpm0
m---------arch/tmux/.config/tmux/plugins/vim-tmux-navigator0
-rw-r--r--arch/tmux/.config/tmux/tmux.conf119
-rw-r--r--arch/wallust/.config/wallust/templates/eww-colors.scss5
-rw-r--r--arch/wallust/.config/wallust/templates/ghostty-colors.conf19
-rw-r--r--arch/wallust/.config/wallust/templates/hyprland-colors.conf10
-rw-r--r--arch/wallust/.config/wallust/templates/launcher-colors.sh10
-rw-r--r--arch/wallust/.config/wallust/templates/mako.conf15
-rw-r--r--arch/wallust/.config/wallust/templates/nvim.lua19
-rw-r--r--arch/wallust/.config/wallust/templates/spicetify-colors.ini19
-rw-r--r--arch/wallust/.config/wallust/wallust.toml14
61 files changed, 2072 insertions, 0 deletions
diff --git a/arch/.stowrc b/arch/.stowrc
new file mode 100644
index 0000000..9a31e3b
--- /dev/null
+++ b/arch/.stowrc
@@ -0,0 +1 @@
+--target=/home/blake
diff --git a/arch/eww/.config/eww/eww.scss b/arch/eww/.config/eww/eww.scss
new file mode 100644
index 0000000..7fd3240
--- /dev/null
+++ b/arch/eww/.config/eww/eww.scss
@@ -0,0 +1,112 @@
+// Colors generated by wallust — see ~/.config/wallust/templates/eww-colors.scss
+// Fallback values used until wallust has run at least once
+$bg: #1e2121;
+$fg: #cdd6f4;
+$accent: #89b4fa;
+$surface: #313244;
+$muted: #6c7086;
+$warn: #f38ba8;
+
+@import "/home/blake/.cache/wallust/eww-colors";
+
+* {
+ font-family: "JetBrainsMono Nerd Font";
+ font-size: 12px;
+ all: unset;
+}
+
+.bar {
+ background-color: transparentize($bg, 0.4);
+ color: $fg;
+ border: none;
+ box-shadow: none;
+}
+
+// ── Left ───────────────────────────────────────────────────────────────────
+
+.left {
+ padding: 0 8px;
+}
+
+.workspaces {
+ padding: 0 2px;
+}
+
+.ws {
+ color: $muted;
+ padding: 0 5px;
+}
+
+.ws.active {
+ color: $accent;
+ font-weight: bold;
+}
+
+// ── Center ─────────────────────────────────────────────────────────────────
+
+.center {
+ padding: 0 8px;
+}
+
+.date {
+ color: $muted;
+}
+
+.clock {
+ color: $fg;
+ font-weight: bold;
+}
+
+.music-icon {
+ color: $accent;
+}
+
+.song {
+ color: $muted;
+}
+
+// ── Right ──────────────────────────────────────────────────────────────────
+
+.right {
+ padding: 0 8px;
+}
+
+.stat {
+ padding: 0 1px;
+}
+
+.stat-label {
+ color: $muted;
+ padding-right: 3px;
+}
+
+.stat-val {
+ color: $fg;
+}
+
+.stat-val.net {
+ color: $muted;
+}
+
+.stat-val.warn {
+ color: $warn;
+}
+
+.stat-btn {
+ all: unset;
+}
+
+.stat-btn:hover .stat-label,
+.stat-btn:hover .stat-val {
+ color: $accent;
+}
+
+.power-btn {
+ color: $muted;
+ padding: 0 4px;
+ margin-left: 2px;
+}
+
+.power-btn:hover {
+ color: $warn;
+}
diff --git a/arch/eww/.config/eww/eww.yuck b/arch/eww/.config/eww/eww.yuck
new file mode 100644
index 0000000..8574e1f
--- /dev/null
+++ b/arch/eww/.config/eww/eww.yuck
@@ -0,0 +1,150 @@
+;; ── Listeners (Hyprland IPC) ──────────────────────────────────────────────
+;; hyprland-workspaces emits JSON arrays with {id, name, windows, isActive}
+;; on every workspace change — single source of truth for list + active state.
+
+(deflisten workspaces :initial "[]"
+ `scripts/workspaces`)
+
+;; ── Polls ──────────────────────────────────────────────────────────────────
+
+(defpoll clock :interval "1s"
+ `date +"%-H:%M"`)
+
+(defpoll date-str :interval "60s"
+ `date +"%a %b %d"`)
+
+;; Show the PLAYING source; fall back to any paused one
+(defpoll player-status :interval "2s"
+ `playerctl -a status 2>/dev/null | grep -q "^Playing" && echo "Playing" || playerctl status 2>/dev/null || echo "Stopped"`)
+
+(defpoll player-song :interval "2s"
+ `playerctl -a metadata --format "{{status}}||{{ artist }} - {{ title }}" 2>/dev/null | grep "^Playing||" | head -1 | sed 's/^Playing||//' || playerctl metadata --format "{{ artist }} - {{ title }}" 2>/dev/null || echo ""`)
+
+(defpoll battery-level :interval "30s" :initial "100"
+ `cat /sys/class/power_supply/BAT0/capacity 2>/dev/null || echo "100"`)
+
+(defpoll battery-status :interval "10s" :initial "Unknown"
+ `cat /sys/class/power_supply/BAT0/status 2>/dev/null || echo "Unknown"`)
+
+(defpoll cpu :interval "3s"
+ `scripts/cpu`)
+
+(defpoll ram :interval "5s"
+ `free | awk '/^Mem/{printf "%d", $3/$2*100}'`)
+
+(defpoll disk :interval "60s"
+ `df / | awk 'NR==2{printf "%d", $3/$2*100}'`)
+
+(defpoll volume :interval "1s"
+ `pamixer --get-volume 2>/dev/null || echo "0"`)
+
+;; Use sysfs operstate — works regardless of network manager backend
+(defpoll wifi-iface :interval "5s"
+ `IFACE=$(ls /sys/class/net | grep -m1 '^wl'); [ -n "$IFACE" ] && [ "$(cat /sys/class/net/$IFACE/operstate 2>/dev/null)" = "up" ] && echo "$IFACE" || echo "disconnected"`)
+
+(defpoll brightness :interval "2s"
+ `brightnessctl -m 2>/dev/null | awk -F, '{print int($4)}' || echo "100"`)
+
+(defpoll netspeed :interval "3s"
+ `scripts/netspeed`)
+
+;; ── Left: Workspaces + Music ───────────────────────────────────────────────
+
+(defwidget workspaces []
+ (box :class "workspaces" :space-evenly false :spacing 5
+ (for ws in workspaces
+ (button
+ :class {ws.active ? "ws active" : "ws"}
+ :onclick "hyprctl dispatch workspace ${ws.id}"
+ {ws.id}))))
+
+(defwidget music []
+ (eventbox
+ :onclick "playerctl play-pause"
+ (box :class "music"
+ :space-evenly false :spacing 5
+ :visible {player-status != "Stopped" && player-status != ""}
+ (label :class "music-icon"
+ :text {player-status == "Playing" ? "▶" : "⏸"})
+ (label :class "song" :text player-song :limit-width 30))))
+
+;; ── Center: Clock ──────────────────────────────────────────────────────────
+
+(defwidget center-section []
+ (box :class "center" :halign "center" :space-evenly false :spacing 8
+ (label :class "date" :text date-str)
+ (label :class "clock" :text clock)))
+
+;; ── Right: Stats ───────────────────────────────────────────────────────────
+
+(defwidget bright-stat []
+ (eventbox
+ :onscroll "[ '{}' = 'up' ] && brightnessctl set +5% || brightnessctl set 5%-"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "bright")
+ (label :class "stat-val" :text "${brightness}%"))))
+
+(defwidget audio-stat []
+ (eventbox
+ :onscroll "[ '{}' = 'up' ] && pamixer --increase 5 || pamixer --decrease 5"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "audio")
+ (label :class "stat-val" :text "${volume}%"))))
+
+(defwidget wifi-stat []
+ (eventbox
+ :onclick "ghostty -e nmtui &"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "wifi")
+ (label :class "stat-val" :text wifi-iface))))
+
+(defwidget cpu-stat []
+ (eventbox
+ :onclick "ghostty -e btop &"
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text "cpu")
+ (label :class "stat-val" :text "${cpu}%"))))
+
+(defwidget right-section []
+ (box :class "right" :halign "end" :space-evenly false :spacing 8
+ (bright-stat)
+ (audio-stat)
+ (label :class "stat-val net" :text netspeed)
+ (wifi-stat)
+ (stat :lbl "bat" :value "${battery-level}%"
+ :class {battery-level < 20 ? "warn" : ""})
+ (stat :lbl "strg" :value "${disk}%")
+ (stat :lbl "ram" :value "${ram}%")
+ (cpu-stat)
+ (button :class "power-btn" :onclick "scripts/powermenu &" "pwr")))
+
+;; Plain stat (non-interactive) ─────────────────────────────────────────────
+
+(defwidget stat [lbl value ?class]
+ (box :class "stat" :space-evenly false :spacing 3
+ (label :class "stat-label" :text lbl)
+ (label :class {class == "" ? "stat-val" : "stat-val ${class}"} :text value)))
+
+;; ── Bar ────────────────────────────────────────────────────────────────────
+
+(defwidget bar []
+ (centerbox :class "bar" :orientation "h"
+ (box :class "left" :halign "start" :space-evenly false :spacing 12
+ (workspaces)
+ (music))
+ (center-section)
+ (right-section)))
+
+(defwindow bar
+ :monitor 0
+ :windowtype "dock"
+ :geometry (geometry
+ :x "0%"
+ :y "0%"
+ :width "100%"
+ :height "24px"
+ :anchor "top center")
+ :stacking "fg"
+ :exclusive true
+ :focusable false
+ (bar))
diff --git a/arch/eww/.config/eww/scripts/active-workspace b/arch/eww/.config/eww/scripts/active-workspace
new file mode 100755
index 0000000..21795b9
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/active-workspace
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+hyprctl activeworkspace -j | jq '.id'
+socat -u UNIX-CONNECT:"${XDG_RUNTIME_DIR}/hypr/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" STDOUT \
+ | while read -r _; do hyprctl activeworkspace -j | jq '.id'; done
diff --git a/arch/eww/.config/eww/scripts/active-ws b/arch/eww/.config/eww/scripts/active-ws
new file mode 100755
index 0000000..c7b3f3e
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/active-ws
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+hyprctl activeworkspace | awk '/^workspace/{print $3}'
diff --git a/arch/eww/.config/eww/scripts/cpu b/arch/eww/.config/eww/scripts/cpu
new file mode 100755
index 0000000..f06ae7b
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/cpu
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+read -r _ u1 _ s1 id1 _ < /proc/stat
+sleep 1
+read -r _ u2 _ s2 id2 _ < /proc/stat
+used=$(( (u2+s2) - (u1+s1) ))
+total=$(( (u2+s2+id2) - (u1+s1+id1) ))
+printf "%d\n" $(( total > 0 ? 100 * used / total : 0 ))
diff --git a/arch/eww/.config/eww/scripts/hypr-socket b/arch/eww/.config/eww/scripts/hypr-socket
new file mode 100755
index 0000000..9012c17
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/hypr-socket
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+"""Stream lines from Hyprland's socket2 IPC."""
+import socket, sys, os
+
+sock_path = (
+ os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
+ + "/hypr/"
+ + os.environ["HYPRLAND_INSTANCE_SIGNATURE"]
+ + "/.socket2.sock"
+)
+
+s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+s.connect(sock_path)
+
+buf = b""
+while True:
+ chunk = s.recv(4096)
+ if not chunk:
+ break
+ buf += chunk
+ while b"\n" in buf:
+ line, buf = buf.split(b"\n", 1)
+ sys.stdout.write(line.decode(errors="replace") + "\n")
+ sys.stdout.flush()
diff --git a/arch/eww/.config/eww/scripts/netspeed b/arch/eww/.config/eww/scripts/netspeed
new file mode 100755
index 0000000..f5bb25b
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/netspeed
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+IFACE=$(ip route show default 2>/dev/null | awk '{print $5; exit}')
+if [[ -z "$IFACE" ]]; then echo "↓0 ↑0 KB/s"; exit; fi
+rx1=$(cat /sys/class/net/"$IFACE"/statistics/rx_bytes 2>/dev/null || echo 0)
+tx1=$(cat /sys/class/net/"$IFACE"/statistics/tx_bytes 2>/dev/null || echo 0)
+sleep 1
+rx2=$(cat /sys/class/net/"$IFACE"/statistics/rx_bytes 2>/dev/null || echo 0)
+tx2=$(cat /sys/class/net/"$IFACE"/statistics/tx_bytes 2>/dev/null || echo 0)
+echo "↓$(( (rx2-rx1)/1024 )) ↑$(( (tx2-tx1)/1024 )) KB/s"
diff --git a/arch/eww/.config/eww/scripts/powermenu b/arch/eww/.config/eww/scripts/powermenu
new file mode 100755
index 0000000..e136674
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/powermenu
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+chosen=$(printf "Suspend\nReboot\nShutdown" | bemenu -p " Power")
+case "$chosen" in
+ Suspend) systemctl suspend ;;
+ Reboot) systemctl reboot ;;
+ Shutdown) systemctl poweroff ;;
+esac
diff --git a/arch/eww/.config/eww/scripts/workspaces b/arch/eww/.config/eww/scripts/workspaces
new file mode 100755
index 0000000..28571a7
--- /dev/null
+++ b/arch/eww/.config/eww/scripts/workspaces
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+# hyprland-workspaces streams JSON arrays on every workspace change.
+# Filter out special/scratchpad workspaces (negative IDs).
+hyprland-workspaces eDP-1 | while IFS= read -r line; do
+ echo "$line" | jq -c '[.[] | select(.id > 0)]'
+done
diff --git a/arch/fish/.config/fish/conf.d/aliases.fish b/arch/fish/.config/fish/conf.d/aliases.fish
new file mode 100644
index 0000000..cab8bad
--- /dev/null
+++ b/arch/fish/.config/fish/conf.d/aliases.fish
@@ -0,0 +1,17 @@
+alias ls 'eza --icons --group-directories-first'
+alias ll 'eza -la --icons --group-directories-first'
+alias cat 'bat'
+alias grep 'rg'
+alias find 'fd'
+alias cd 'z'
+alias y 'yazi'
+alias tl 'tldr'
+# alias info 'pinfo' # uncomment once pinfo is installed (AUR: pinfo)
+
+# Man page shortcuts
+abbr -a m1 'man -s 1'
+abbr -a m2 'man -s 2'
+abbr -a m3 'man -s 3'
+abbr -a m5 'man -s 5'
+abbr -a m7 'man -s 7'
+abbr -a m8 'man -s 8'
diff --git a/arch/fish/.config/fish/conf.d/edit-configs.fish b/arch/fish/.config/fish/conf.d/edit-configs.fish
new file mode 100644
index 0000000..c085a41
--- /dev/null
+++ b/arch/fish/.config/fish/conf.d/edit-configs.fish
@@ -0,0 +1,12 @@
+# ── Config editing shortcuts ──────────────────────────────────────────────────
+set -l dots ~/dotfiles/arch
+
+alias envim "nvim $dots/nvim/.config/nvim/"
+alias etmux "nvim $dots/tmux/.config/tmux/tmux.conf"
+alias ehypr "nvim $dots/hyprland/.config/hypr/"
+alias efish "nvim $dots/fish/.config/fish/config.fish"
+alias eghostty "nvim $dots/ghostty/.config/ghostty/config"
+alias ewwconf "nvim $dots/eww/.config/eww/"
+alias emako "nvim $dots/mako/.config/mako/config"
+alias ewallust "nvim $dots/wallust/.config/wallust/"
+alias edots "nvim $dots/"
diff --git a/arch/fish/.config/fish/config.fish b/arch/fish/.config/fish/config.fish
new file mode 100644
index 0000000..6c8baba
--- /dev/null
+++ b/arch/fish/.config/fish/config.fish
@@ -0,0 +1,25 @@
+set fish_greeting
+
+# Auto-attach to tmux on interactive startup (skip if already inside tmux)
+# if status is-interactive && not set -q TMUX
+# exec tmux new-session
+# end
+
+set -gx EDITOR nvim
+set -gx BROWSER zen-browser
+
+fish_add_path ~/.local/bin
+
+# Starship prompt
+starship init fish | source
+
+# Zoxide (replaces cd)
+zoxide init fish | source
+
+# Direnv
+direnv hook fish | source
+
+# FZF
+# Wallust: set FZF_DEFAULT_OPTS with colors from ~/.cache/wallust/fzf-colors when wiring theming
+set -gx FZF_DEFAULT_COMMAND 'fd --type f --hidden --follow --exclude .git'
+set -gx FZF_DEFAULT_OPTS '--height 40% --layout=reverse --border'
diff --git a/arch/fish/.config/fish/fish_variables b/arch/fish/.config/fish/fish_variables
new file mode 100644
index 0000000..612abe8
--- /dev/null
+++ b/arch/fish/.config/fish/fish_variables
@@ -0,0 +1,4 @@
+# This file contains fish universal variable definitions.
+# VERSION: 3.0
+SETUVAR __fish_initialized:4300
+SETUVAR fish_user_paths:/home/blake/\x2elocal/bin
diff --git a/arch/ghostty/.config/ghostty/config b/arch/ghostty/.config/ghostty/config
new file mode 100644
index 0000000..11decd3
--- /dev/null
+++ b/arch/ghostty/.config/ghostty/config
@@ -0,0 +1,21 @@
+font-family = JetBrainsMono Nerd Font
+font-size = 12
+
+scrollback-limit = 10000
+
+shell-integration = fish
+command = /usr/bin/fish
+
+window-padding-x = 8
+window-padding-y = 8
+
+mouse-hide-while-typing = true
+
+# Ctrl+Backspace sends C-h by default; remap to C-w (backward-kill-word)
+keybind = ctrl+backspace=text:\x17
+confirm-close-surface = false
+
+background-opacity = 0.85
+background-blur-radius = 20
+
+config-file = /home/blake/.cache/wallust/ghostty-colors.conf
diff --git a/arch/git/.config/git/config b/arch/git/.config/git/config
new file mode 100644
index 0000000..80978cd
--- /dev/null
+++ b/arch/git/.config/git/config
@@ -0,0 +1,21 @@
+[user]
+ name = "steven-na"
+ email = "noreply.github@stvnc.dev"
+
+[core]
+ editor = nvim
+
+[init]
+ defaultBranch = main
+
+[pull]
+ rebase = true
+
+[push]
+ autoSetupRemote = true
+
+[merge]
+ conflictstyle = diff3
+
+[diff]
+ colorMoved = default
diff --git a/arch/hyprland/.config/hypr/hypridle.conf b/arch/hyprland/.config/hypr/hypridle.conf
new file mode 100644
index 0000000..c6e2ac8
--- /dev/null
+++ b/arch/hyprland/.config/hypr/hypridle.conf
@@ -0,0 +1,32 @@
+general {
+ lock_cmd = pidof hyprlock || hyprlock
+ before_sleep_cmd = loginctl lock-session
+ after_sleep_cmd = hyprctl dispatch dpms on
+ ignore_dbus_inhibit = false
+}
+
+# Dim screen after 2.5 min
+listener {
+ timeout = 150
+ on-timeout = brightnessctl -s set 10%
+ on-resume = brightnessctl -r
+}
+
+# Lock after 5 min
+listener {
+ timeout = 300
+ on-timeout = loginctl lock-session
+}
+
+# Display off after 5.5 min
+listener {
+ timeout = 330
+ on-timeout = hyprctl dispatch dpms off
+ on-resume = hyprctl dispatch dpms on
+}
+
+# Suspend after 30 min
+listener {
+ timeout = 1800
+ on-timeout = systemctl suspend
+}
diff --git a/arch/hyprland/.config/hypr/hyprland.conf b/arch/hyprland/.config/hypr/hyprland.conf
new file mode 100644
index 0000000..65c7338
--- /dev/null
+++ b/arch/hyprland/.config/hypr/hyprland.conf
@@ -0,0 +1,86 @@
+source = ~/.config/hypr/monitors.conf
+source = ~/.config/hypr/theme.conf
+source = ~/.config/hypr/keybinds.conf
+source = ~/.config/hypr/windowrules.conf
+
+# NVIDIA
+env = WLR_NO_HARDWARE_CURSORS,1
+env = EGL_PLATFORM,wayland
+env = OZONE_PLATFORM,wayland
+env = XCURSOR_SIZE,24
+env = XCURSOR_THEME,Bibata-Modern-Classic
+
+general {
+ layout = dwindle
+ gaps_in = 3
+ gaps_out = 3
+ border_size = 2
+ # border colors are in theme.conf
+}
+
+dwindle {
+ pseudotile = true
+ preserve_split = true
+}
+
+input {
+ kb_layout = us
+ follow_mouse = 1
+ special_fallthrough = true
+ touchpad {
+ natural_scroll = false
+ }
+}
+
+binds {
+ movefocus_cycles_fullscreen = 1
+}
+
+cursor {
+ no_hardware_cursors = 1
+}
+
+misc {
+ disable_hyprland_logo = true
+ disable_splash_rendering = true
+ font_family = JetBrainsMono Nerd Font
+ vfr = 1
+ vrr = 1
+ enable_swallow = true
+}
+
+decoration {
+ rounding = 4
+ active_opacity = 1.0
+ inactive_opacity = 0.92
+ blur {
+ enabled = true
+ size = 6
+ passes = 3
+ vibrancy = 0.2
+ special = true
+ new_optimizations = true
+ }
+ shadow {
+ enabled = true
+ range = 4
+ render_power = 3
+ }
+}
+
+animations {
+ enabled = true
+ bezier = myBezier, 0.05, 0.97, 0.05, 0.97
+ animation = windows, 1, 4, myBezier
+ animation = windowsOut, 1, 4, default, popin 80%
+ animation = fade, 1, 4, default
+ animation = workspaces, 1, 4, default
+}
+
+exec-once = awww-daemon
+exec-once = mako
+exec-once = hypridle
+exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
+exec-once = eww daemon && eww open bar
+exec-once = bash -c 'sleep 0.5; [ -f ~/.cache/wallust/last-wallpaper ] && ~/.local/bin/set-wallpaper "$(cat ~/.cache/wallust/last-wallpaper)"'
+exec-once = spicetify watch -s
diff --git a/arch/hyprland/.config/hypr/hyprlock.conf b/arch/hyprland/.config/hypr/hyprlock.conf
new file mode 100644
index 0000000..24dd752
--- /dev/null
+++ b/arch/hyprland/.config/hypr/hyprlock.conf
@@ -0,0 +1,52 @@
+general {
+ hide_cursor = true
+ no_fade_in = false
+ disable_loading_bar = true
+ ignore_empty_input = true
+}
+
+background {
+ monitor =
+ # Wallust: replace with path = ~/.cache/wallust/last-wallpaper
+ path = screenshot
+ blur_passes = 3
+ blur_size = 7
+ brightness = 0.5
+}
+
+label {
+ monitor =
+ text = cmd[update:1000] date +"%-H:%M"
+ font_size = 72
+ font_family = JetBrainsMono Nerd Font
+ color = rgba(cdd6f4ff)
+ position = 0, 200
+ halign = center
+ valign = center
+}
+
+label {
+ monitor =
+ text = cmd[update:60000] date +"%A, %B %d"
+ font_size = 18
+ font_family = JetBrainsMono Nerd Font
+ color = rgba(cdd6f4cc)
+ position = 0, 120
+ halign = center
+ valign = center
+}
+
+input-field {
+ monitor =
+ size = 300, 48
+ outline_thickness = 2
+ outer_color = rgba(cdd6f4ff)
+ inner_color = rgba(1e1e2ecc)
+ font_color = rgba(cdd6f4ff)
+ fade_on_empty = true
+ placeholder_text = 󰌾 Password
+ font_family = JetBrainsMono Nerd Font
+ position = 0, -80
+ halign = center
+ valign = center
+}
diff --git a/arch/hyprland/.config/hypr/keybinds.conf b/arch/hyprland/.config/hypr/keybinds.conf
new file mode 100644
index 0000000..2f00c8f
--- /dev/null
+++ b/arch/hyprland/.config/hypr/keybinds.conf
@@ -0,0 +1,106 @@
+$mainMod = SUPER
+
+# ── Core ──────────────────────────────────────────────────────────────────────
+bind = $mainMod, Q, exec, ghostty
+bind = $mainMod, C, killactive
+bind = $mainMod, J, togglesplit
+bind = $mainMod CTRL, V, togglefloating
+bind = $mainMod, F, fullscreen, 1
+bind = $mainMod CTRL, F, fullscreen, 0
+bind = $mainMod SHIFT, L, exec, loginctl lock-session
+bind = $mainMod, R, exec, ~/.local/bin/launcher
+bind = $mainMod, W, exec, ghostty --class=float-term -e ~/.local/bin/pick-wallpaper
+bind = $mainMod SHIFT, F, exec, zen-browser
+
+# Keyboard pointer modes (wl-kbptr)
+bind = $mainMod, G, exec, pkill wl-kbptr || true; wl-kbptr -o modes=floating,click -o mode_floating.source=detect
+bind = $mainMod, M, exec, pkill wl-kbptr || true; wl-kbptr -o modes=tile,bisect,click
+
+# Clipboard
+bind = $mainMod SHIFT, V, exec, cliphist list | bemenu | cliphist decode | wl-copy
+
+# ── Focus movement ────────────────────────────────────────────────────────────
+bind = $mainMod, left, movefocus, l
+bind = $mainMod, right, movefocus, r
+bind = $mainMod, up, movefocus, u
+bind = $mainMod, down, movefocus, d
+bind = $mainMod, h, movefocus, l
+bind = $mainMod, l, movefocus, r
+bind = $mainMod, k, movefocus, u
+bind = $mainMod, j, movefocus, d
+
+# ── Mouse scroll wheel movement ───────────────────────────────────────────────
+binde = $mainMod, B, exec, ydotool mousemove --wheel -- 0 1
+binde = $mainMod, V, exec, ydotool mousemove --wheel -- 0 -1
+
+# ── Workspaces ────────────────────────────────────────────────────────────────
+bind = $mainMod, 1, workspace, 1
+bind = $mainMod, 2, workspace, 2
+bind = $mainMod, 3, workspace, 3
+bind = $mainMod, 4, workspace, 4
+bind = $mainMod, 5, workspace, 5
+bind = $mainMod, 6, workspace, 6
+bind = $mainMod, 7, workspace, 7
+bind = $mainMod, 8, workspace, 8
+bind = $mainMod, 9, workspace, 9
+bind = $mainMod, 0, workspace, 10
+
+bind = $mainMod SHIFT, 1, movetoworkspace, 1
+bind = $mainMod SHIFT, 2, movetoworkspace, 2
+bind = $mainMod SHIFT, 3, movetoworkspace, 3
+bind = $mainMod SHIFT, 4, movetoworkspace, 4
+bind = $mainMod SHIFT, 5, movetoworkspace, 5
+bind = $mainMod SHIFT, 6, movetoworkspace, 6
+bind = $mainMod SHIFT, 7, movetoworkspace, 7
+bind = $mainMod SHIFT, 8, movetoworkspace, 8
+bind = $mainMod SHIFT, 9, movetoworkspace, 9
+bind = $mainMod SHIFT, 0, movetoworkspace, 10
+
+bind = $mainMod, mouse_down, workspace, e+1
+bind = $mainMod, mouse_up, workspace, e-1
+
+# ── Special workspaces (scratchpads) ─────────────────────────────────────────
+bind = $mainMod, S, togglespecialworkspace, magic
+bind = $mainMod SHIFT, S, movetoworkspace, special:magic
+
+# ── Screenshots ───────────────────────────────────────────────────────────────
+bind = , Print, exec, grim -g "$(slurp)" - | wl-copy
+bind = $mainMod, Print, exec, grim - | wl-copy
+bind = $mainMod SHIFT, Print, exec, grim -g "$(hyprctl activewindow -j | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')" - | wl-copy
+
+# ── Resize submap ─────────────────────────────────────────────────────────────
+bind = $mainMod CTRL, R, submap, resize
+
+submap = resize
+binde = , h, resizeactive, -20 0
+binde = , j, resizeactive, 0 20
+binde = , k, resizeactive, 0 -20
+binde = , l, resizeactive, 20 0
+binde = , left, resizeactive, -20 0
+binde = , down, resizeactive, 0 20
+binde = , up, resizeactive, 0 -20
+binde = , right, resizeactive, 20 0
+bind = , Escape, submap, reset
+bind = , Return, submap, reset
+bind = , catchall, submap, reset
+submap = reset
+
+# ── Mouse window management ───────────────────────────────────────────────────
+bindm = $mainMod, mouse:272, movewindow
+bindm = $mainMod CTRL, mouse:272, movewindow
+bindm = $mainMod, mouse:273, resizewindow
+
+# ── Media keys ────────────────────────────────────────────────────────────────
+bindl = , XF86AudioPlay, exec, playerctl play-pause
+bindl = , XF86AudioNext, exec, playerctl next
+bindl = , XF86AudioPrev, exec, playerctl previous
+bindl = , XF86AudioMute, exec, pamixer -t
+
+bindle = , XF86AudioRaiseVolume, exec, pamixer -i 5
+bindle = , XF86AudioLowerVolume, exec, pamixer -d 5
+bindle = , XF86MonBrightnessUp, exec, brightnessctl set +5%
+bindle = , XF86MonBrightnessDown, exec, brightnessctl set 5%-
+
+bind = $mainMod, F2, exec, brightnessctl -d asus::kbd_backlight set 1-
+bind = $mainMod, F3, exec, brightnessctl -d asus::kbd_backlight set +1
+bind = $mainMod, F4, exec, asusctl aura effect --next-mode
diff --git a/arch/hyprland/.config/hypr/monitors.conf b/arch/hyprland/.config/hypr/monitors.conf
new file mode 100644
index 0000000..6acb585
--- /dev/null
+++ b/arch/hyprland/.config/hypr/monitors.conf
@@ -0,0 +1,8 @@
+# Laptop screen
+monitor = desc:BOE NE160WUM-NX2, 1920x1200@165, 0x0, 1
+
+# Fallback for any other connected display
+monitor = , preferred, auto, 1
+
+# Multi-monitor: uncomment and configure when adding external display
+# monitor = desc:YOUR_MONITOR_DESC, 1920x1080@144, auto, 1, transform, 1
diff --git a/arch/hyprland/.config/hypr/theme.conf b/arch/hyprland/.config/hypr/theme.conf
new file mode 100644
index 0000000..0107198
--- /dev/null
+++ b/arch/hyprland/.config/hypr/theme.conf
@@ -0,0 +1,7 @@
+# Theme / color configuration — generated by wallust
+# Run set-wallpaper <path> to regenerate
+#
+# If ~/.cache/wallust/hyprland-colors.conf doesn't exist yet (first boot),
+# the fallback colors below are used until you set a wallpaper.
+
+source = ~/.cache/wallust/hyprland-colors.conf
diff --git a/arch/hyprland/.config/hypr/windowrules.conf b/arch/hyprland/.config/hypr/windowrules.conf
new file mode 100644
index 0000000..6778846
--- /dev/null
+++ b/arch/hyprland/.config/hypr/windowrules.conf
@@ -0,0 +1,62 @@
+# ── Spotify transparency ──────────────────────────────────────────────────────
+# Barely-noticeable opacity lets Hyprland's compositor blur show wallpaper
+# bleed-through behind the CSS rgba panels in the spicetify theme.
+windowrule {
+ name = spotify-transparency
+ match:class = Spotify
+ opacity = 0.90 override
+}
+
+# ── Floating terminal (Super+Q with float-term class) ─────────────────────────
+windowrule {
+ name = float-term
+ match:class = float-term
+ float = true
+ center = true
+ stay_focused = true
+ size = 900 600
+}
+
+# ── Wallpaper picker ─────────────────────────────────────────────────────────
+windowrule {
+ name = wallpaper-picker
+ match:class = wallpaper-picker
+ float = true
+ center = true
+ size = 1200 700
+}
+
+# ── bemenu launcher window ────────────────────────────────────────────────────
+windowrule {
+ name = launcher
+ match:class = launcher
+ float = true
+ center = true
+ size = 800 500
+}
+
+# ── Suppress maximize requests ────────────────────────────────────────────────
+windowrule {
+ name = suppress-maximize
+ match:class = .*
+ suppress_event = maximize
+}
+
+# ── Fix XWayland drag artifacts ───────────────────────────────────────────────
+windowrule {
+ name = fix-xwayland-drags
+ match:class = ^$
+ match:title = ^$
+ match:xwayland = true
+ match:float = true
+ match:fullscreen = false
+ match:pin = false
+ no_focus = true
+}
+
+# ── Inhibit idle when fullscreen ─────────────────────────────────────────────
+windowrule {
+ name = no-idle-fullscreen
+ match:fullscreen = true
+ idle_inhibit = fullscreen
+}
diff --git a/arch/install.sh b/arch/install.sh
new file mode 100755
index 0000000..c7b24bc
--- /dev/null
+++ b/arch/install.sh
@@ -0,0 +1,191 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Usage: ./install.sh [-y]
+# -y Auto-accept all optional component prompts
+
+AUTO_YES=false
+while getopts "y" opt; do
+ case $opt in
+ y) AUTO_YES=true ;;
+ esac
+done
+
+ask() {
+ # ask <prompt> — returns 0 (yes) or 1 (no). Always yes if -y was passed.
+ if $AUTO_YES; then return 0; fi
+ read -rp "$1 [y/N] " ans
+ [[ "${ans,,}" == "y" ]]
+}
+
+# ── Core WM ───────────────────────────────────────────────────────────────────
+pacman_packages=(
+ hyprland
+ hyprlock
+ hypridle
+ xdg-desktop-portal-hyprland
+ xdg-desktop-portal-gtk
+ polkit-gnome
+)
+
+# ── Audio ─────────────────────────────────────────────────────────────────────
+pacman_packages+=(
+ pipewire
+ pipewire-alsa
+ pipewire-pulse
+ pipewire-jack
+ wireplumber
+ pamixer
+ playerctl
+ pavucontrol
+)
+
+# ── Display / input ───────────────────────────────────────────────────────────
+pacman_packages+=(
+ grim
+ slurp
+ wl-clipboard
+ brightnessctl
+ ydotool
+ chafa
+)
+
+# ── Notifications ─────────────────────────────────────────────────────────────
+pacman_packages+=(
+ mako
+ libnotify
+)
+
+# ── Launcher ──────────────────────────────────────────────────────────────────
+pacman_packages+=(
+ bemenu
+ bemenu-wayland
+)
+
+# ── Shell / CLI ───────────────────────────────────────────────────────────────
+pacman_packages+=(
+ fish
+ tmux
+ starship
+ zoxide
+ fzf
+ ripgrep
+ fd
+ bat
+ eza
+ btop
+ yazi
+ direnv
+ tealdeer
+ jq
+ cliphist
+)
+
+# ── Fonts ─────────────────────────────────────────────────────────────────────
+pacman_packages+=(
+ ttf-jetbrains-mono-nerd
+ noto-fonts
+ noto-fonts-emoji
+ noto-fonts-cjk
+)
+
+# ── GTK / cursor ──────────────────────────────────────────────────────────────
+pacman_packages+=(
+ papirus-icon-theme
+ gnome-themes-extra
+ gnome-keyring
+ gsettings-desktop-schemas
+)
+
+# ── Bluetooth ─────────────────────────────────────────────────────────────────
+pacman_packages+=(
+ bluez
+ bluez-utils
+)
+
+# ── Network / misc ────────────────────────────────────────────────────────────
+pacman_packages+=(
+ networkmanager
+ openssh
+ wget
+ socat
+ curl
+ git
+)
+
+# ── AUR packages ──────────────────────────────────────────────────────────────
+aur_packages=(
+ ghostty # terminal
+ sesh # tmux session manager
+ eww # bar
+ adw-gtk3 # GTK theme
+ bibata-cursor-theme
+ wl-kbptr # keyboard pointer (Super+G / Super+M binds)
+ pinfo # info page viewer (used in fish aliases)
+ asusctl # keyboard backlight
+ wallust # dynamic color generation from wallpaper
+ hyprland-workspaces # eww workspace widget with active-state JSON
+ socat # Hyprland IPC streaming (eww deflisten scripts)
+)
+
+# ── Install core ──────────────────────────────────────────────────────────────
+echo "==> Installing pacman packages..."
+sudo pacman -S --needed "${pacman_packages[@]}"
+
+echo "==> Installing AUR packages (via yay)..."
+yay -S --needed "${aur_packages[@]}"
+
+# ── Optional: Spotify + Spicetify ────────────────────────────────────────────
+echo ""
+if ask "==> Install Spotify + Spicetify (wallust auto-theming)?"; then
+ yay -S --needed spotify spicetify-cli
+
+ echo "==> Fixing Spotify permissions for Spicetify..."
+ sudo chmod a+wr /opt/spotify
+ sudo chmod -R a+wr /opt/spotify/Apps
+
+ echo "==> Applying Spicetify wallust theme..."
+ spicetify config current_theme wallust color_scheme Base
+ spicetify backup apply
+
+ echo " Spotify + Spicetify installed and themed."
+fi
+
+# ── Optional: Neovim dev environment ─────────────────────────────────────────
+echo ""
+if ask "==> Install Neovim dev environment (LSP, formatters, linters)?"; then
+ sudo pacman -S --needed \
+ neovim \
+ clang \
+ lua-language-server \
+ stylua \
+ python-black \
+ python-isort \
+ cppcheck \
+ ruff
+
+ yay -S --needed \
+ pyright \
+ haskell-language-server
+
+ echo "==> Stowing nvim config..."
+ cd ~/dotfiles/arch && stow nvim
+
+ echo " Neovim dev environment installed."
+ echo " Open nvim and run :Lazy to install plugins."
+fi
+
+# ── Groups ────────────────────────────────────────────────────────────────────
+# input → ydotool (/dev/uinput access)
+# video → brightnessctl (screen + keyboard backlight without sudo)
+echo ""
+echo "==> Adding $(whoami) to groups: input, video..."
+sudo usermod -aG input,video "$(whoami)"
+
+echo ""
+echo "==> Done. Next steps:"
+echo " cd ~/dotfiles/arch"
+echo " rm ~/.config/hypr/hyprland.conf # remove autogenerated config"
+echo " stow hyprland ghostty eww fish mako git scripts wallust spicetify nvim"
+echo " chsh -s /usr/bin/fish"
+echo " Log out and back in for group changes to take effect"
diff --git a/arch/mako/.config/mako/config b/arch/mako/.config/mako/config
new file mode 100644
index 0000000..8d9d037
--- /dev/null
+++ b/arch/mako/.config/mako/config
@@ -0,0 +1,15 @@
+font=JetBrainsMono Nerd Font 12
+border-size=2
+border-radius=4
+padding=8,12
+max-icon-size=48
+default-timeout=5000
+ignore-timeout=1
+
+background-color=#211C1A
+text-color=#FCF5EA
+border-color=#9E7E6D
+
+[urgency=high]
+border-color=#7F5F52
+background-color=#211C1A
diff --git a/arch/nvim/.config/nvim/.stylua.toml b/arch/nvim/.config/nvim/.stylua.toml
new file mode 100644
index 0000000..e55efd5
--- /dev/null
+++ b/arch/nvim/.config/nvim/.stylua.toml
@@ -0,0 +1,3 @@
+indent_type = "Spaces"
+indent_width = 4
+column_width = 120
diff --git a/arch/nvim/.config/nvim/init.lua b/arch/nvim/.config/nvim/init.lua
new file mode 100644
index 0000000..fb4cadd
--- /dev/null
+++ b/arch/nvim/.config/nvim/init.lua
@@ -0,0 +1,27 @@
+-- Bootstrap lazy.nvim
+local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
+if not vim.uv.fs_stat(lazypath) then
+ vim.fn.system({
+ "git", "clone", "--filter=blob:none",
+ "https://github.com/folke/lazy.nvim.git",
+ "--branch=stable", lazypath,
+ })
+end
+vim.opt.rtp:prepend(lazypath)
+
+require("options")
+require("keymaps")
+
+require("lazy").setup("plugins", {
+ performance = {
+ rtp = {
+ disabled_plugins = {
+ "gzip", "matchit", "matchparen", "netrwPlugin",
+ "tarPlugin", "tohtml", "tutor", "zipPlugin",
+ },
+ },
+ },
+ install = { colorscheme = { "habamax" } },
+ checker = { enabled = false },
+ change_detection = { notify = false },
+})
diff --git a/arch/nvim/.config/nvim/lazy-lock.json b/arch/nvim/.config/nvim/lazy-lock.json
new file mode 100644
index 0000000..3480e8d
--- /dev/null
+++ b/arch/nvim/.config/nvim/lazy-lock.json
@@ -0,0 +1,30 @@
+{
+ "LuaSnip": { "branch": "master", "commit": "a62e1083a3cfe8b6b206e7d3d33a51091df25357" },
+ "blink.cmp": { "branch": "main", "commit": "78336bc89ee5365633bcf754d93df01678b5c08f" },
+ "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" },
+ "conform.nvim": { "branch": "master", "commit": "dca1a190aa85f9065979ef35802fb77131911106" },
+ "fidget.nvim": { "branch": "main", "commit": "889e2e96edef4e144965571d46f7a77bcc4d0ddf" },
+ "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
+ "fzf-lua": { "branch": "main", "commit": "1ae863c83e0823225b4e041845f10c918d5fb882" },
+ "gitsigns.nvim": { "branch": "main", "commit": "6d808f99bd63303646794406e270bd553ad7792e" },
+ "hardtime.nvim": { "branch": "main", "commit": "b4e431934af1fe224a3a801f632c008278cb7628" },
+ "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
+ "lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
+ "leap.nvim": { "branch": "main", "commit": "ebecd4c7957770e21abe71f8b2dd34952850033d" },
+ "lualine.nvim": { "branch": "master", "commit": "a905eeebc4e63fdc48b5135d3bf8aea5618fb21c" },
+ "mini.base16": { "branch": "main", "commit": "800f107b27a00978b0dadd672d6da21a5b12fa9a" },
+ "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
+ "nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
+ "nvim-lint": { "branch": "master", "commit": "eab58b48eb11d7745c11c505e0f3057165902461" },
+ "nvim-lspconfig": { "branch": "master", "commit": "8fde495949782bb61c2605174e231d145a048d8c" },
+ "nvim-surround": { "branch": "main", "commit": "61319d4bd1c5e336e197defa15bd104c51f0fb29" },
+ "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
+ "nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
+ "oil.nvim": { "branch": "master", "commit": "0fcc83805ad11cf714a949c98c605ed717e0b83e" },
+ "promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
+ "snacks.nvim": { "branch": "main", "commit": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e" },
+ "undotree": { "branch": "master", "commit": "6fa6b57cda8459e1e4b2ca34df702f55242f4e4d" },
+ "vim-tmux-navigator": { "branch": "master", "commit": "e41c431a0c7b7388ae7ba341f01a0d217eb3a432" },
+ "vim-tpipeline": { "branch": "master", "commit": "9c1fc6a9aed96a6ff5f5cb0420e3760e95dfb1c1" },
+ "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
+}
diff --git a/arch/nvim/.config/nvim/lua/keymaps.lua b/arch/nvim/.config/nvim/lua/keymaps.lua
new file mode 100644
index 0000000..2a38a95
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/keymaps.lua
@@ -0,0 +1,41 @@
+vim.g.mapleader = " "
+vim.g.maplocalleader = " "
+
+local map = vim.keymap.set
+
+-- Disable Space in normal/visual (used as leader)
+map({ "n", "v" }, "<Space>", "<Nop>", { silent = true })
+
+-- Save
+map({ "n", "i" }, "<C-s>", "<cmd>w<CR>", { desc = "Save" })
+
+-- Clear search highlight
+map("n", "<Esc>", "<cmd>nohlsearch<CR>")
+
+-- Exit terminal mode
+map("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal" })
+
+-- Better up/down on wrapped lines
+map({ "n", "v" }, "j", "gj")
+map({ "n", "v" }, "k", "gk")
+
+-- Buffer navigation
+map("n", "<S-h>", "<cmd>bprevious<CR>", { desc = "Prev buffer" })
+map("n", "<S-l>", "<cmd>bnext<CR>", { desc = "Next buffer" })
+map("n", "<leader>bd", "<cmd>bdelete<CR>", { desc = "Delete buffer" })
+
+-- Splits
+map("n", "<leader>sv", "<C-w>v", { desc = "Split vertical" })
+map("n", "<leader>sh", "<C-w>s", { desc = "Split horizontal" })
+map("n", "<leader>se", "<C-w>=", { desc = "Equal splits" })
+map("n", "<leader>sx", "<cmd>close<CR>", { desc = "Close split" })
+
+-- Move lines
+map("n", "<A-Down>", "<cmd>m .+1<CR>==", { desc = "Move line down" })
+map("n", "<A-Up>", "<cmd>m .-2<CR>==", { desc = "Move line up" })
+map("v", "<A-Down>", ":m '>+1<CR>gv=gv", { desc = "Move selection down" })
+map("v", "<A-Up>", ":m '<-2<CR>gv=gv", { desc = "Move selection up" })
+
+-- Stay in visual after indent
+map("v", "<", "<gv")
+map("v", ">", ">gv")
diff --git a/arch/nvim/.config/nvim/lua/options.lua b/arch/nvim/.config/nvim/lua/options.lua
new file mode 100644
index 0000000..9a97b4b
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/options.lua
@@ -0,0 +1,39 @@
+local opt = vim.opt
+
+-- Line numbers
+opt.number = true
+opt.relativenumber = true
+
+-- Indentation
+opt.expandtab = true
+opt.tabstop = 4
+opt.shiftwidth = 4
+opt.softtabstop = 4
+
+-- Visual
+opt.cursorline = true
+opt.scrolloff = 10
+opt.signcolumn = "yes"
+opt.list = true
+opt.listchars = { tab = " ", trail = "·", nbsp = "␣" }
+opt.termguicolors = true
+opt.colorcolumn = ""
+
+-- Behaviour
+opt.swapfile = false
+opt.undofile = true
+opt.updatetime = 250
+opt.timeoutlen = 300
+opt.splitbelow = true
+opt.splitright = true
+opt.ignorecase = true
+opt.smartcase = true
+opt.inccommand = "split"
+opt.hlsearch = true
+opt.breakindent = true
+
+-- Folding (nvim-ufo)
+opt.foldcolumn = "0"
+opt.foldlevel = 99
+opt.foldlevelstart = 99
+opt.foldenable = true
diff --git a/arch/nvim/.config/nvim/lua/plugins/colorscheme.lua b/arch/nvim/.config/nvim/lua/plugins/colorscheme.lua
new file mode 100644
index 0000000..8198402
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/colorscheme.lua
@@ -0,0 +1,74 @@
+return {
+ "echasnovski/mini.base16",
+ version = false,
+ lazy = false,
+ priority = 1000,
+ config = function()
+ local palette_path = vim.fn.expand("~/.cache/wallust/nvim.lua")
+
+ local fallback = {
+ base00 = "#1e1e2e", base01 = "#181825", base02 = "#313244",
+ base03 = "#45475a", base04 = "#585b70", base05 = "#cdd6f4",
+ base06 = "#f5c2e7", base07 = "#b4befe", base08 = "#f38ba8",
+ base09 = "#fab387", base0A = "#f9e2af", base0B = "#a6e3a1",
+ base0C = "#94e2d5", base0D = "#89b4fa", base0E = "#cba6f7",
+ base0F = "#f2cdcd",
+ }
+
+ local function load_palette()
+ local ok, p = pcall(dofile, palette_path)
+ return ok and p or fallback
+ end
+
+ local transparent_groups = {
+ "Normal", "NormalNC", "NormalFloat",
+ "SignColumn", "FoldColumn", "EndOfBuffer",
+ "LineNr", "CursorLineNr", "CursorLine",
+ "StatusLine", "StatusLineNC",
+ "TabLine", "TabLineFill", "TabLineSel",
+ "VertSplit", "WinSeparator",
+ "Pmenu", "PmenuSbar", "PmenuThumb",
+ "FloatBorder", "FloatTitle",
+ }
+
+ local function apply_transparent()
+ for _, grp in ipairs(transparent_groups) do
+ vim.cmd("hi " .. grp .. " guibg=NONE ctermbg=NONE")
+ end
+ end
+
+ local function apply(palette)
+ require("mini.base16").setup({ palette = palette })
+ vim.api.nvim_set_hl(0, "LineNr", { fg = palette.base05 })
+ vim.api.nvim_set_hl(0, "LineNrAbove", { fg = palette.base05 })
+ vim.api.nvim_set_hl(0, "LineNrBelow", { fg = palette.base05 })
+ vim.api.nvim_set_hl(0, "CursorLineNr", { fg = palette.base03, bold = true })
+ if vim.g.transparent_bg then
+ apply_transparent()
+ end
+ end
+
+ vim.g.transparent_bg = false
+ apply(load_palette())
+
+ -- Watch wallust output file and reload automatically
+ local watcher = vim.uv.new_fs_event()
+ if watcher then
+ watcher:start(palette_path, {}, vim.schedule_wrap(function()
+ apply(load_palette())
+ end))
+ end
+
+ -- Toggle: wipe background colors so the terminal bg shows through
+ vim.keymap.set("n", "<leader>tt", function()
+ vim.g.transparent_bg = not vim.g.transparent_bg
+ if vim.g.transparent_bg then
+ apply_transparent()
+ vim.notify("Background: transparent", vim.log.levels.INFO)
+ else
+ apply(load_palette())
+ vim.notify("Background: colorscheme", vim.log.levels.INFO)
+ end
+ end, { desc = "Toggle transparent background" })
+ end,
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/completion.lua b/arch/nvim/.config/nvim/lua/plugins/completion.lua
new file mode 100644
index 0000000..26ab3b6
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/completion.lua
@@ -0,0 +1,40 @@
+return {
+ {
+ "saghen/blink.cmp",
+ version = "*",
+ dependencies = { "L3MON4D3/LuaSnip", "rafamadriz/friendly-snippets" },
+ opts = {
+ keymap = {
+ preset = "none",
+ ["<C-Space>"] = { "show", "show_documentation", "hide_documentation" },
+ ["<C-e>"] = { "hide" },
+ ["<CR>"] = { "accept", "fallback" },
+ ["<Tab>"] = { "select_next", "snippet_forward", "fallback" },
+ ["<S-Tab>"] = { "select_prev", "snippet_backward", "fallback" },
+ ["<C-b>"] = { "scroll_documentation_up", "fallback" },
+ ["<C-f>"] = { "scroll_documentation_down", "fallback" },
+ ["<C-n>"] = { "select_next", "fallback" },
+ ["<C-p>"] = { "select_prev", "fallback" },
+ },
+ appearance = { nerd_font_variant = "mono" },
+ sources = {
+ default = { "lsp", "path", "snippets", "buffer" },
+ },
+ snippets = { preset = "luasnip" },
+ completion = {
+ documentation = { auto_show = true, auto_show_delay_ms = 200 },
+ ghost_text = { enabled = true },
+ menu = { border = "single" },
+ },
+ signature = { enabled = true, window = { border = "single" } },
+ },
+ },
+ {
+ "L3MON4D3/LuaSnip",
+ event = "InsertEnter",
+ dependencies = { "rafamadriz/friendly-snippets" },
+ config = function()
+ require("luasnip.loaders.from_vscode").lazy_load()
+ end,
+ },
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/editor.lua b/arch/nvim/.config/nvim/lua/plugins/editor.lua
new file mode 100644
index 0000000..a6ec0e0
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/editor.lua
@@ -0,0 +1,73 @@
+return {
+ -- Motion: s + two chars to jump anywhere
+ {
+ url = "https://codeberg.org/andyg/leap.nvim",
+ keys = {
+ { "s", "<Plug>(leap)", mode = { "n", "x", "o" }, desc = "Leap" },
+ { "S", "<Plug>(leap-from-window)", mode = { "n", "o" }, desc = "Leap from window" },
+ },
+ },
+ -- Enforce good vim habits
+ {
+ "m4xshen/hardtime.nvim",
+ event = "VeryLazy",
+ dependencies = { "MunifTanjim/nui.nvim" },
+ opts = {
+ max_count = 4,
+ -- Remove arrows from disabled (completely blocked) and add to
+ -- restricted (blocked after max_count, same as hjkl)
+ disabled_keys = {
+ ["<Up>"] = {},
+ ["<Down>"] = {},
+ ["<Left>"] = {},
+ ["<Right>"] = {},
+ },
+ restricted_keys = {
+ ["<Up>"] = { "n", "x" },
+ ["<Down>"] = { "n", "x" },
+ ["<Left>"] = { "n", "x" },
+ ["<Right>"] = { "n", "x" },
+ },
+ },
+ },
+ -- Auto-close brackets/quotes
+ {
+ "windwp/nvim-autopairs",
+ event = "InsertEnter",
+ opts = { check_ts = true },
+ },
+ -- Folding powered by treesitter/LSP
+ {
+ "kevinhwang91/nvim-ufo",
+ dependencies = { "kevinhwang91/promise-async" },
+ event = "BufReadPost",
+ keys = {
+ {
+ "zR",
+ function()
+ require("ufo").openAllFolds()
+ end,
+ desc = "Open all folds",
+ },
+ {
+ "zM",
+ function()
+ require("ufo").closeAllFolds()
+ end,
+ desc = "Close all folds",
+ },
+ },
+ opts = {
+ provider_selector = function()
+ return { "treesitter", "indent" }
+ end,
+ },
+ },
+ -- Surround text objects: ys, ds, cs
+ {
+ "kylechui/nvim-surround",
+ version = "*",
+ event = "VeryLazy",
+ opts = {},
+ },
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/extras.lua b/arch/nvim/.config/nvim/lua/plugins/extras.lua
new file mode 100644
index 0000000..95366ed
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/extras.lua
@@ -0,0 +1,37 @@
+return {
+ -- Splash screen / dashboard
+ {
+ "folke/snacks.nvim",
+ priority = 1000,
+ lazy = false,
+ opts = {
+ dashboard = {
+ sections = {
+ { section = "header" },
+ { section = "keys", gap = 1, padding = 1 },
+ { section = "startup" },
+ },
+ },
+ },
+ },
+ -- Undo history tree
+ {
+ "mbbill/undotree",
+ keys = {
+ { "<leader>u", "<cmd>UndotreeToggle<CR>", desc = "Undo tree" },
+ },
+ },
+ -- Buffer-based file explorer
+ {
+ "stevearc/oil.nvim",
+ lazy = false,
+ keys = {
+ { "<leader>e", "<cmd>Oil<CR>", desc = "File explorer" },
+ { "-", "<cmd>Oil<CR>", desc = "Open parent dir" },
+ },
+ opts = {
+ default_file_explorer = true,
+ view_options = { show_hidden = true },
+ },
+ },
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/formatting.lua b/arch/nvim/.config/nvim/lua/plugins/formatting.lua
new file mode 100644
index 0000000..912a6a8
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/formatting.lua
@@ -0,0 +1,47 @@
+return {
+ "stevearc/conform.nvim",
+ event = "BufWritePre",
+ cmd = { "ConformInfo", "FormatToggle" },
+ keys = {
+ {
+ "<leader>cF",
+ function()
+ vim.g.format_on_save = not vim.g.format_on_save
+ vim.notify(
+ "Format on save: " .. (vim.g.format_on_save and "enabled" or "disabled"),
+ vim.log.levels.INFO,
+ { title = "conform" }
+ )
+ end,
+ desc = "Toggle autoformat",
+ },
+ {
+ "<leader>cf",
+ function() require("conform").format({ async = true, lsp_fallback = true }) end,
+ desc = "Format buffer",
+ },
+ },
+ config = function()
+ vim.g.format_on_save = true
+
+ require("conform").setup({
+ formatters_by_ft = {
+ c = { "clang_format" },
+ cpp = { "clang_format" },
+ python = { "isort", "black" },
+ lua = { "stylua" },
+ haskell = { "ormolu" },
+ },
+ format_on_save = function(bufnr)
+ if not vim.g.format_on_save then return end
+ if vim.g.disable_autoformat_buf and vim.g.disable_autoformat_buf[bufnr] then return end
+ return { timeout_ms = 500, lsp_fallback = true }
+ end,
+ })
+
+ vim.api.nvim_create_user_command("FormatToggle", function()
+ vim.g.format_on_save = not vim.g.format_on_save
+ vim.notify("Format on save: " .. (vim.g.format_on_save and "on" or "off"))
+ end, {})
+ end,
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/fzf.lua b/arch/nvim/.config/nvim/lua/plugins/fzf.lua
new file mode 100644
index 0000000..e507348
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/fzf.lua
@@ -0,0 +1,23 @@
+return {
+ "ibhagwan/fzf-lua",
+ cmd = "FzfLua",
+ keys = {
+ { "<leader>ff", "<cmd>FzfLua files<CR>", desc = "Find files" },
+ { "<leader>fg", "<cmd>FzfLua live_grep<CR>", desc = "Live grep" },
+ { "<leader>fb", "<cmd>FzfLua buffers<CR>", desc = "Buffers" },
+ { "<leader>fs", "<cmd>FzfLua grep_cword<CR>", desc = "Grep word under cursor" },
+ { "<leader>fr", "<cmd>FzfLua oldfiles<CR>", desc = "Recent files" },
+ { "<leader>fc", "<cmd>FzfLua commands<CR>", desc = "Commands" },
+ { "<leader>fd", "<cmd>FzfLua diagnostics_document<CR>", desc = "Document diagnostics" },
+ { "<leader>/", "<cmd>FzfLua blines<CR>", desc = "Search current buffer" },
+ },
+ opts = {
+ winopts = {
+ height = 0.6,
+ width = 0.8,
+ border = "single",
+ preview = { border = "single" },
+ },
+ fzf_opts = { ["--cycle"] = true },
+ },
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/linting.lua b/arch/nvim/.config/nvim/lua/plugins/linting.lua
new file mode 100644
index 0000000..bbbeaa7
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/linting.lua
@@ -0,0 +1,17 @@
+return {
+ "mfussenegger/nvim-lint",
+ event = { "BufReadPost", "BufWritePost", "InsertLeave" },
+ config = function()
+ require("lint").linters_by_ft = {
+ c = { "cppcheck" },
+ cpp = { "cppcheck" },
+ python = { "ruff" },
+ }
+
+ vim.api.nvim_create_autocmd({ "BufWritePost", "BufReadPost", "InsertLeave" }, {
+ callback = function()
+ require("lint").try_lint()
+ end,
+ })
+ end,
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/lsp.lua b/arch/nvim/.config/nvim/lua/plugins/lsp.lua
new file mode 100644
index 0000000..17c5c64
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/lsp.lua
@@ -0,0 +1,75 @@
+return {
+ {
+ "neovim/nvim-lspconfig",
+ event = { "BufReadPre", "BufNewFile" },
+ dependencies = {
+ "saghen/blink.cmp",
+ {
+ "folke/lazydev.nvim",
+ ft = "lua",
+ opts = { library = { { path = "luvit-meta/library", words = { "vim%.uv" } } } },
+ },
+ },
+ config = function()
+ -- nvim 0.11+ API: vim.lsp.config / vim.lsp.enable
+ -- nvim-lspconfig provides default server configs in its runtime lsp/*.lua files
+
+ local caps = require("blink.cmp").get_lsp_capabilities()
+
+ -- Apply capabilities to all servers
+ vim.lsp.config("*", { capabilities = caps })
+
+ -- Server-specific overrides
+ vim.lsp.config("lua_ls", {
+ settings = {
+ Lua = {
+ diagnostics = { globals = { "vim" } },
+ workspace = { checkThirdParty = false },
+ },
+ },
+ })
+
+ -- Enable servers (default configs come from nvim-lspconfig runtime)
+ vim.lsp.enable({ "clangd", "pyright", "lua_ls", "hls" })
+
+ -- Keymaps on attach
+ vim.api.nvim_create_autocmd("LspAttach", {
+ callback = function(ev)
+ local map = function(keys, func, desc)
+ vim.keymap.set("n", keys, func, { buffer = ev.buf, desc = "LSP: " .. desc })
+ end
+ map("gd", vim.lsp.buf.definition, "Goto definition")
+ map("gD", vim.lsp.buf.declaration, "Goto declaration")
+ map("gr", vim.lsp.buf.references, "Goto references")
+ map("gI", vim.lsp.buf.implementation,"Goto implementation")
+ map("K", vim.lsp.buf.hover, "Hover docs")
+ map("<leader>rn", vim.lsp.buf.rename, "Rename")
+ map("<leader>ca", vim.lsp.buf.code_action, "Code action")
+ map("[d", vim.diagnostic.goto_prev, "Prev diagnostic")
+ map("]d", vim.diagnostic.goto_next, "Next diagnostic")
+ map("<leader>e", vim.diagnostic.open_float, "Show diagnostic")
+ end,
+ })
+
+ vim.diagnostic.config({
+ virtual_text = false,
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = " ",
+ [vim.diagnostic.severity.WARN] = " ",
+ [vim.diagnostic.severity.INFO] = " ",
+ [vim.diagnostic.severity.HINT] = " ",
+ },
+ },
+ float = { border = "single" },
+ underline = true,
+ update_in_insert = false,
+ })
+ end,
+ },
+ {
+ "j-hui/fidget.nvim",
+ event = "LspAttach",
+ opts = { notification = { window = { winblend = 0 } } },
+ },
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/nav.lua b/arch/nvim/.config/nvim/lua/plugins/nav.lua
new file mode 100644
index 0000000..827da07
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/nav.lua
@@ -0,0 +1,16 @@
+return {
+ "christoomey/vim-tmux-navigator",
+ cmd = {
+ "TmuxNavigateLeft", "TmuxNavigateDown",
+ "TmuxNavigateUp", "TmuxNavigateRight",
+ },
+ keys = {
+ { "<C-Left>", "<cmd>TmuxNavigateLeft<CR>", desc = "Pane left", mode = { "n", "t" } },
+ { "<C-Down>", "<cmd>TmuxNavigateDown<CR>", desc = "Pane down", mode = { "n", "t" } },
+ { "<C-Up>", "<cmd>TmuxNavigateUp<CR>", desc = "Pane up", mode = { "n", "t" } },
+ { "<C-Right>", "<cmd>TmuxNavigateRight<CR>", desc = "Pane right", mode = { "n", "t" } },
+ },
+ init = function()
+ vim.g.tmux_navigator_no_mappings = 1
+ end,
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/treesitter.lua b/arch/nvim/.config/nvim/lua/plugins/treesitter.lua
new file mode 100644
index 0000000..b31fd1c
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/treesitter.lua
@@ -0,0 +1,34 @@
+return {
+ "nvim-treesitter/nvim-treesitter",
+ build = ":TSUpdate",
+ event = { "BufReadPre", "BufNewFile" },
+ opts = {
+ ensure_installed = {
+ "c",
+ "cpp",
+ "python",
+ "lua",
+ "haskell",
+ "vim",
+ "vimdoc",
+ "query",
+ "bash",
+ "markdown",
+ "markdown_inline",
+ "json",
+ "toml",
+ "yaml",
+ },
+ highlight = { enable = true },
+ indent = { enable = true },
+ auto_install = false,
+ },
+ config = function(_, opts)
+ local ok = pcall(require, "nvim-treesitter.configs")
+ if ok then
+ require("nvim-treesitter.configs").setup(opts)
+ else
+ require("nvim-treesitter").setup(opts)
+ end
+ end,
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/ui.lua b/arch/nvim/.config/nvim/lua/plugins/ui.lua
new file mode 100644
index 0000000..f0f8a27
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/ui.lua
@@ -0,0 +1,105 @@
+return {
+ -- Statusline
+ {
+ "nvim-lualine/lualine.nvim",
+ event = "VeryLazy",
+ config = function()
+ local function make_theme()
+ local ok, p = pcall(dofile, vim.fn.expand("~/.cache/wallust/nvim.lua"))
+ if not ok then
+ p = { base0D = "#89b4fa", base05 = "#cdd6f4", base04 = "#585b70",
+ base0B = "#a6e3a1", base0E = "#cba6f7", base08 = "#f38ba8", base0A = "#f9e2af" }
+ end
+ return {
+ normal = { a = { fg = p.base0D, bg = "NONE", gui = "bold" }, b = { fg = p.base05, bg = "NONE" }, c = { fg = p.base04, bg = "NONE" } },
+ insert = { a = { fg = p.base0B, bg = "NONE", gui = "bold" } },
+ visual = { a = { fg = p.base0E, bg = "NONE", gui = "bold" } },
+ replace = { a = { fg = p.base08, bg = "NONE", gui = "bold" } },
+ command = { a = { fg = p.base0A, bg = "NONE", gui = "bold" } },
+ inactive = { a = { fg = p.base04, bg = "NONE" }, b = { fg = p.base04, bg = "NONE" }, c = { fg = p.base04, bg = "NONE" } },
+ }
+ end
+
+ -- lualine.setup() (and every ColorScheme reload of it) asserts
+ -- laststatus=3. Register the guard first so it catches every future
+ -- write, then immediately fix the one we're about to trigger.
+ vim.api.nvim_create_autocmd("OptionSet", {
+ pattern = "laststatus",
+ callback = function()
+ if vim.o.laststatus ~= 0 then
+ vim.o.laststatus = 0
+ end
+ end,
+ })
+
+ require("lualine").setup({
+ options = {
+ theme = make_theme(),
+ globalstatus = true,
+ component_separators = { left = "|", right = "|" },
+ section_separators = { left = "", right = "" },
+ },
+ sections = {
+ lualine_a = { "mode" },
+ lualine_b = { "branch", "diff", "diagnostics" },
+ lualine_c = { { "filename", path = 1 } },
+ lualine_x = { "encoding", "filetype" },
+ lualine_y = { "progress" },
+ lualine_z = { "location" },
+ },
+ })
+ vim.o.laststatus = 0 -- synchronous fix for the setup() call above
+
+ vim.api.nvim_create_autocmd("ColorScheme", {
+ callback = function()
+ require("lualine").setup({ options = { theme = make_theme() } })
+ vim.o.laststatus = 0
+ end,
+ })
+ end,
+ },
+ -- Pipe lualine into the tmux status bar
+ {
+ "vimpostor/vim-tpipeline",
+ event = "VeryLazy",
+ init = function()
+ vim.g.tpipeline_restore = 0 -- clear files on exit rather than restore broken format
+ vim.g.tpipeline_embedopts = {} -- tmux.conf owns status-left/right format
+ end,
+ },
+ -- Buffer tabs
+ {
+ "akinsho/bufferline.nvim",
+ version = "*",
+ event = "VeryLazy",
+ keys = {
+ { "<leader>bp", "<cmd>BufferLinePick<CR>", desc = "Pick buffer" },
+ { "<leader>bo", "<cmd>BufferLineCloseOthers<CR>", desc = "Close others" },
+ { "<leader>bc", "<cmd>BufferLinePickClose<CR>", desc = "Close (pick)" },
+ },
+ opts = {
+ options = {
+ mode = "buffers",
+ show_buffer_close_icons = false,
+ show_close_icon = false,
+ separator_style = "thin",
+ always_show_bufferline = false,
+ diagnostics = "nvim_lsp",
+ },
+ },
+ },
+ -- Git signs in the gutter
+ {
+ "lewis6991/gitsigns.nvim",
+ event = { "BufReadPre", "BufNewFile" },
+ opts = {
+ signs = {
+ add = { text = "▎" },
+ change = { text = "▎" },
+ delete = { text = "" },
+ topdelete = { text = "" },
+ changedelete = { text = "▎" },
+ },
+ },
+ },
+}
diff --git a/arch/nvim/.config/nvim/lua/plugins/whichkey.lua b/arch/nvim/.config/nvim/lua/plugins/whichkey.lua
new file mode 100644
index 0000000..97e8b19
--- /dev/null
+++ b/arch/nvim/.config/nvim/lua/plugins/whichkey.lua
@@ -0,0 +1,25 @@
+return {
+ "folke/which-key.nvim",
+ event = "VeryLazy",
+ opts = {
+ icons = { mappings = false },
+ delay = 300,
+ spec = {
+ { "<leader>f", group = "find" },
+ { "<leader>b", group = "buffer" },
+ { "<leader>s", group = "split" },
+ { "<leader>r", group = "refactor" },
+ { "<leader>c", group = "code" },
+ { "<leader>cF", desc = "Toggle autoformat" },
+ { "<leader>cf", desc = "Format buffer" },
+ { "<leader>e", desc = "File explorer" },
+ { "<leader>u", desc = "Undo tree" },
+ { "<leader>bp", desc = "Pick buffer" },
+ { "<leader>bo", desc = "Close others" },
+ { "<leader>bc", desc = "Close (pick)" },
+ },
+ },
+ keys = {
+ { "<leader>?", function() require("which-key").show({ global = false }) end, desc = "Buffer keymaps" },
+ },
+}
diff --git a/arch/scripts/.local/bin/launcher b/arch/scripts/.local/bin/launcher
new file mode 100755
index 0000000..9451d35
--- /dev/null
+++ b/arch/scripts/.local/bin/launcher
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# Application launcher using bemenu — colors sourced from wallust cache
+
+FONT="JetBrainsMono Nerd Font 12"
+
+# Defaults (used until wallust has run at least once)
+NB="#1e1e2e" NF="#cdd6f4" SB="#89b4fa" SF="#1e1e2e"
+AB="#313244" AF="#cdd6f4" TB="#1e1e2e" TF="#89b4fa"
+FB="#1e1e2e" FF="#cdd6f4"
+
+[ -f ~/.cache/wallust/launcher-colors.sh ] && source ~/.cache/wallust/launcher-colors.sh
+
+bemenu-run \
+ --fn "$FONT" \
+ --nb "$NB" \
+ --nf "$NF" \
+ --sb "$SB" \
+ --sf "$SF" \
+ --ab "$AB" \
+ --af "$AF" \
+ --tb "$TB" \
+ --tf "$TF" \
+ --fb "$FB" \
+ --ff "$FF" \
+ --list 10 \
+ --prompt " "
diff --git a/arch/scripts/.local/bin/pick-wallpaper b/arch/scripts/.local/bin/pick-wallpaper
new file mode 100755
index 0000000..c96ca4e
--- /dev/null
+++ b/arch/scripts/.local/bin/pick-wallpaper
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+WALLPAPER_DIR="${HOME}/wallpapers"
+
+selected=$(find "$WALLPAPER_DIR" -type f \( \
+ -name "*.jpg" -o -name "*.jpeg" -o \
+ -name "*.png" -o -name "*.webp" \) | sort | \
+ fzf --preview 'chafa --size=80x40 {}' \
+ --preview-window 'right:60%' \
+ --prompt "wallpaper> ")
+
+[ -n "$selected" ] && ~/.local/bin/set-wallpaper "$selected"
diff --git a/arch/scripts/.local/bin/set-wallpaper b/arch/scripts/.local/bin/set-wallpaper
new file mode 100755
index 0000000..30b15d8
--- /dev/null
+++ b/arch/scripts/.local/bin/set-wallpaper
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+set -euo pipefail
+WALLPAPER="${1:?usage: set-wallpaper <path>}"
+
+awww img "$WALLPAPER" \
+ --transition-type wipe \
+ --transition-angle 30 \
+ --transition-duration 1
+
+wallust run "$WALLPAPER" --ignore-sequence background --ignore-sequence foreground
+
+gdbus call --session \
+ --dest com.mitchellh.ghostty \
+ --object-path /com/mitchellh/ghostty \
+ --method org.gtk.Actions.Activate \
+ "reload-config" "[]" "{}" > /dev/null 2>&1 || true
+
+hyprctl reload
+pkill -x mako || true
+mako &
+eww reload || true
+
+# spicetify watch -s picks up color.ini changes automatically.
+# Fallback: refresh manually if watch isn't running.
+pgrep -f "spicetify watch" > /dev/null || spicetify refresh > /dev/null 2>&1 || true
+
+# nvim picks up the new palette automatically via a libuv file watcher in colorscheme.lua
diff --git a/arch/spicetify/.config/spicetify/Themes/wallust/color.ini b/arch/spicetify/.config/spicetify/Themes/wallust/color.ini
new file mode 100644
index 0000000..a0c7209
--- /dev/null
+++ b/arch/spicetify/.config/spicetify/Themes/wallust/color.ini
@@ -0,0 +1,19 @@
+[Base]
+text = FCF5EA
+subtext = F2E8D9
+main = 211C1A
+main-elevated = 4D4744
+highlight = 4D4744
+highlight-elevated = AAA298
+sidebar = 211C1A
+player = 211C1A
+card = 4D4744
+shadow = 000000
+selected-row = 4D4744
+button = 9E7E6D
+button-active = B9AA93
+button-disabled = AAA298
+tab-active = 4D4744
+notification = 9E7E6D
+notification-error = 7F5F52
+misc = AAA298
diff --git a/arch/spicetify/.config/spicetify/Themes/wallust/user.css b/arch/spicetify/.config/spicetify/Themes/wallust/user.css
new file mode 100644
index 0000000..8e4e406
--- /dev/null
+++ b/arch/spicetify/.config/spicetify/Themes/wallust/user.css
@@ -0,0 +1,72 @@
+/* Wallust — auto-updating spicetify theme
+ Colors come from color.ini (generated by wallust on each wallpaper change).
+ --spice-rgb-* variables are auto-derived by spicetify from color.ini values.
+ Transparency: rgba() + backdrop-filter for frosted glass within the window.
+ The Hyprland opacity windowrule (0.95) makes the window slightly transparent
+ so wallpaper bleed-through is visible behind the blurred panels. */
+
+/* ── Root ─────────────────────────────────────────────────────────────────── */
+html, body {
+ background: transparent !important;
+}
+
+/* ── Main content area ────────────────────────────────────────────────────── */
+.Root__main-view {
+ background: rgba(var(--spice-rgb-main), 0.88) !important;
+ backdrop-filter: blur(20px) saturate(160%);
+}
+
+/* ── Left sidebar / nav ───────────────────────────────────────────────────── */
+.Root__nav-bar,
+.root-nav-bar {
+ background: rgba(var(--spice-rgb-sidebar), 0.88) !important;
+ backdrop-filter: blur(20px) saturate(160%);
+}
+
+/* ── Bottom now-playing bar ───────────────────────────────────────────────── */
+.Root__now-playing-bar {
+ background: rgba(var(--spice-rgb-player), 0.92) !important;
+ backdrop-filter: blur(20px) saturate(160%);
+ border-top: 1px solid rgba(var(--spice-rgb-highlight), 0.3);
+}
+
+/* ── Top bar ──────────────────────────────────────────────────────────────── */
+.Root__top-bar {
+ background: rgba(var(--spice-rgb-main), 0.80) !important;
+ backdrop-filter: blur(20px) saturate(160%);
+}
+
+/* ── Cards ────────────────────────────────────────────────────────────────── */
+.main-card-card,
+[data-testid="card-image-hit-area"],
+.main-shelf-shelf {
+ background: rgba(var(--spice-rgb-card), 0.60) !important;
+ backdrop-filter: blur(8px);
+ transition: background 0.2s ease;
+}
+
+.main-card-card:hover {
+ background: rgba(var(--spice-rgb-highlight), 0.70) !important;
+}
+
+/* ── Context menus ────────────────────────────────────────────────────────── */
+.main-contextMenu-menu,
+ul[data-radix-popper-content-wrapper] {
+ background: rgba(var(--spice-rgb-main-elevated), 0.94) !important;
+ backdrop-filter: blur(24px) saturate(160%);
+ border: 1px solid rgba(var(--spice-rgb-highlight), 0.25);
+}
+
+/* ── Modals / dialogs ─────────────────────────────────────────────────────── */
+.GenericModal,
+.main-genericModal-overlay,
+[role="dialog"] {
+ background: rgba(var(--spice-rgb-main), 0.92) !important;
+ backdrop-filter: blur(24px);
+}
+
+/* ── Queue / right panel ──────────────────────────────────────────────────── */
+.Root__right-sidebar {
+ background: rgba(var(--spice-rgb-sidebar), 0.88) !important;
+ backdrop-filter: blur(20px) saturate(160%);
+}
diff --git a/arch/tmux/.config/tmux/plugins/tmux-resurrect b/arch/tmux/.config/tmux/plugins/tmux-resurrect
new file mode 160000
+Subproject cff343cf9e81983d3da0c8562b01616f12e8d54
diff --git a/arch/tmux/.config/tmux/plugins/tmux-sensible b/arch/tmux/.config/tmux/plugins/tmux-sensible
new file mode 160000
+Subproject 25cb91f42d020f675bb0a2ce3fbd3a5d96119ef
diff --git a/arch/tmux/.config/tmux/plugins/tmux-yank b/arch/tmux/.config/tmux/plugins/tmux-yank
new file mode 160000
+Subproject acfd36e4fcba99f8310a7dfb432111c242fe739
diff --git a/arch/tmux/.config/tmux/plugins/tpm b/arch/tmux/.config/tmux/plugins/tpm
new file mode 160000
+Subproject 99469c4a9b1ccf77fade25842dc7bafbc8ce994
diff --git a/arch/tmux/.config/tmux/plugins/vim-tmux-navigator b/arch/tmux/.config/tmux/plugins/vim-tmux-navigator
new file mode 160000
+Subproject e41c431a0c7b7388ae7ba341f01a0d217eb3a43
diff --git a/arch/tmux/.config/tmux/tmux.conf b/arch/tmux/.config/tmux/tmux.conf
new file mode 100644
index 0000000..38e15a9
--- /dev/null
+++ b/arch/tmux/.config/tmux/tmux.conf
@@ -0,0 +1,119 @@
+# ── Prefix ────────────────────────────────────────────────────────────────────
+unbind C-b
+set -g prefix C-Space
+bind C-Space send-prefix
+
+# ── Core settings ─────────────────────────────────────────────────────────────
+set -g default-terminal "tmux-256color"
+set -as terminal-overrides ",*:Tc"
+set -as terminal-features ",*:RGB"
+set -g default-shell /usr/bin/fish
+set -g mouse on
+set -g base-index 1
+set -g pane-base-index 1
+set -g renumber-windows on
+set -g history-limit 50000
+set -sg escape-time 0
+set -g focus-events on
+set -g mode-keys emacs
+
+# ── Splits (open in current directory) ───────────────────────────────────────
+unbind %
+unbind '"'
+bind v split-window -h -c "#{pane_current_path}"
+bind h split-window -v -c "#{pane_current_path}"
+
+# ── Pane navigation (Ctrl+arrow, no prefix — vim-tmux-navigator aware) ───────
+# Checks if the active pane is running nvim; if so passes the key to nvim,
+# otherwise switches tmux panes. Neovim needs matching keybinds — see comment
+# at the bottom of this file.
+is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
+ | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?|fzf)(diff)?$'"
+
+bind -n C-Left if-shell "$is_vim" 'send-keys C-Left' 'select-pane -L'
+bind -n C-Down if-shell "$is_vim" 'send-keys C-Down' 'select-pane -D'
+bind -n C-Up if-shell "$is_vim" 'send-keys C-Up' 'select-pane -U'
+bind -n C-Right if-shell "$is_vim" 'send-keys C-Right' 'select-pane -R'
+
+# ── Pane resize (Prefix + Shift+arrow, repeatable) ───────────────────────────
+bind -r S-Up resize-pane -U 5
+bind -r S-Down resize-pane -D 5
+bind -r S-Left resize-pane -L 5
+bind -r S-Right resize-pane -R 5
+
+# ── Window navigation (Alt+arrow, no prefix needed) ──────────────────────────
+bind -n M-Left previous-window
+bind -n M-Right next-window
+
+# ── Sesh: fuzzy session + directory picker ────────────────────────────────────
+bind S display-popup -E -w 80% -h 70% \
+ "sesh connect \$(sesh list | fzf \
+ --height 100% \
+ --reverse \
+ --border \
+ --border-label ' sesh ' \
+ --prompt ' ' \
+ --no-sort)"
+
+# ── Reload config ─────────────────────────────────────────────────────────────
+bind r source-file ~/.config/tmux/tmux.conf \; display " config reloaded"
+
+# ── Pane transparency — empty style lets ghostty background show through ───────
+set -g window-style ''
+set -g window-active-style ''
+
+# ── Status bar ────────────────────────────────────────────────────────────────
+set -g status on
+set -g status-position bottom
+set -g status-interval 1
+set -g status-left-length 99
+set -g status-right-length 99
+set -g status-justify centre
+set -g status-style "bg=default,fg=default"
+
+# Left: session name + nvim lualine left sections (mode, branch, diff)
+set -g status-left "#[fg=colour4,bold] #S #[default] #(cat #{socket_path}-\#{session_id}-vimbridge)"
+set -g status-left-length 80
+# Right: nvim lualine right sections (encoding, filetype, progress, location)
+set -g status-right '#(cat #{socket_path}-\#{session_id}-vimbridge-R)'
+set -g status-right-length 120
+
+# Window list
+set -g window-status-format " #I #W "
+set -g window-status-current-format "#[fg=colour4,bold,reverse] #I #W "
+set -g window-status-separator ""
+
+# ── Borders ───────────────────────────────────────────────────────────────────
+# colour4 = wallust accent (updates automatically when ghostty palette changes)
+set -g pane-border-style "fg=colour0"
+set -g pane-active-border-style "fg=colour4"
+set -g pane-border-lines single
+
+# ── Popups / messages ─────────────────────────────────────────────────────────
+set -g message-style "bg=colour4,fg=colour0,bold"
+set -g popup-border-style "fg=colour4"
+set -g popup-border-lines rounded
+
+
+# ── Plugins ───────────────────────────────────────────────────────────────────
+set -g @plugin 'tmux-plugins/tpm'
+set -g @plugin 'tmux-plugins/tmux-sensible'
+set -g @plugin 'christoomey/vim-tmux-navigator'
+set -g @plugin 'tmux-plugins/tmux-yank'
+set -g @plugin 'tmux-plugins/tmux-resurrect'
+
+# Disable vim-tmux-navigator's default C-h/j/k/l bindings (using arrows instead)
+set -g @vim-tmux-navigator-no-wrap 1
+set -g @vim_tmux_navigator_no_mappings 1
+
+run '~/.tmux/plugins/tpm/tpm'
+
+# vim-tmux-navigator binds C-h to select-pane -L after TPM loads.
+# Unbind it here so Ctrl+Backspace (which sends C-h) passes through to the shell.
+unbind -n C-h
+
+# ── Neovim keybinds (add to your nvim config when ready) ─────────────────────
+# vim.keymap.set({'n','t'}, '<C-Left>', '<Cmd>TmuxNavigateLeft<CR>')
+# vim.keymap.set({'n','t'}, '<C-Down>', '<Cmd>TmuxNavigateDown<CR>')
+# vim.keymap.set({'n','t'}, '<C-Up>', '<Cmd>TmuxNavigateUp<CR>')
+# vim.keymap.set({'n','t'}, '<C-Right>', '<Cmd>TmuxNavigateRight<CR>')
diff --git a/arch/wallust/.config/wallust/templates/eww-colors.scss b/arch/wallust/.config/wallust/templates/eww-colors.scss
new file mode 100644
index 0000000..782b2fa
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/eww-colors.scss
@@ -0,0 +1,5 @@
+$bg: {{background}};
+$fg: {{foreground}};
+$accent: {{color4}};
+$surface: {{color0}};
+$muted: {{color8}};
diff --git a/arch/wallust/.config/wallust/templates/ghostty-colors.conf b/arch/wallust/.config/wallust/templates/ghostty-colors.conf
new file mode 100644
index 0000000..dfa89c5
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/ghostty-colors.conf
@@ -0,0 +1,19 @@
+background = {{background}}
+foreground = {{foreground}}
+
+palette = 0={{color0}}
+palette = 1={{color1}}
+palette = 2={{color2}}
+palette = 3={{color3}}
+palette = 4={{color4}}
+palette = 5={{color5}}
+palette = 6={{color6}}
+palette = 7={{color7}}
+palette = 8={{color8}}
+palette = 9={{color9}}
+palette = 10={{color10}}
+palette = 11={{color11}}
+palette = 12={{color12}}
+palette = 13={{color13}}
+palette = 14={{color14}}
+palette = 15={{color15}}
diff --git a/arch/wallust/.config/wallust/templates/hyprland-colors.conf b/arch/wallust/.config/wallust/templates/hyprland-colors.conf
new file mode 100644
index 0000000..18e7f3d
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/hyprland-colors.conf
@@ -0,0 +1,10 @@
+general {
+ col.active_border = rgba({{ color4 | replace("#", "") }}ee)
+ col.inactive_border = rgba({{ color0 | replace("#", "") }}aa)
+}
+
+decoration {
+ shadow {
+ color = rgba({{ background | replace("#", "") }}ee)
+ }
+}
diff --git a/arch/wallust/.config/wallust/templates/launcher-colors.sh b/arch/wallust/.config/wallust/templates/launcher-colors.sh
new file mode 100644
index 0000000..35b03ee
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/launcher-colors.sh
@@ -0,0 +1,10 @@
+NB="{{background}}"
+NF="{{foreground}}"
+SB="{{color4}}"
+SF="{{background}}"
+AB="{{color0}}"
+AF="{{foreground}}"
+TB="{{background}}"
+TF="{{color4}}"
+FB="{{background}}"
+FF="{{foreground}}"
diff --git a/arch/wallust/.config/wallust/templates/mako.conf b/arch/wallust/.config/wallust/templates/mako.conf
new file mode 100644
index 0000000..d5d8382
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/mako.conf
@@ -0,0 +1,15 @@
+font=JetBrainsMono Nerd Font 12
+border-size=2
+border-radius=4
+padding=8,12
+max-icon-size=48
+default-timeout=5000
+ignore-timeout=1
+
+background-color={{background}}
+text-color={{foreground}}
+border-color={{color4}}
+
+[urgency=high]
+border-color={{color1}}
+background-color={{background}}
diff --git a/arch/wallust/.config/wallust/templates/nvim.lua b/arch/wallust/.config/wallust/templates/nvim.lua
new file mode 100644
index 0000000..2458113
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/nvim.lua
@@ -0,0 +1,19 @@
+-- Generated by wallust — do not edit manually
+return {
+ base00 = "{{background}}", -- bg
+ base01 = "{{color0}}", -- bg highlight
+ base02 = "{{color8}}", -- selection
+ base03 = "{{color8}}", -- comments
+ base04 = "{{color7}}", -- dark fg
+ base05 = "{{foreground}}", -- default fg
+ base06 = "{{color15}}", -- light fg
+ base07 = "{{color15}}", -- bright white
+ base08 = "{{color1}}", -- red / errors
+ base09 = "{{color9}}", -- orange / constants
+ base0A = "{{color3}}", -- yellow / classes
+ base0B = "{{color2}}", -- green / strings
+ base0C = "{{color6}}", -- cyan / support
+ base0D = "{{color4}}", -- blue / functions
+ base0E = "{{color5}}", -- purple / keywords
+ base0F = "{{color13}}", -- brown / embedded
+}
diff --git a/arch/wallust/.config/wallust/templates/spicetify-colors.ini b/arch/wallust/.config/wallust/templates/spicetify-colors.ini
new file mode 100644
index 0000000..65df225
--- /dev/null
+++ b/arch/wallust/.config/wallust/templates/spicetify-colors.ini
@@ -0,0 +1,19 @@
+[Base]
+text = {{ foreground | replace("#", "") }}
+subtext = {{ color7 | replace("#", "") }}
+main = {{ background | replace("#", "") }}
+main-elevated = {{ color0 | replace("#", "") }}
+highlight = {{ color0 | replace("#", "") }}
+highlight-elevated = {{ color8 | replace("#", "") }}
+sidebar = {{ background | replace("#", "") }}
+player = {{ background | replace("#", "") }}
+card = {{ color0 | replace("#", "") }}
+shadow = 000000
+selected-row = {{ color0 | replace("#", "") }}
+button = {{ color4 | replace("#", "") }}
+button-active = {{ color6 | replace("#", "") }}
+button-disabled = {{ color8 | replace("#", "") }}
+tab-active = {{ color0 | replace("#", "") }}
+notification = {{ color4 | replace("#", "") }}
+notification-error = {{ color1 | replace("#", "") }}
+misc = {{ color8 | replace("#", "") }}
diff --git a/arch/wallust/.config/wallust/wallust.toml b/arch/wallust/.config/wallust/wallust.toml
new file mode 100644
index 0000000..ab62dec
--- /dev/null
+++ b/arch/wallust/.config/wallust/wallust.toml
@@ -0,0 +1,14 @@
+backend = "full"
+color_space = "lchmixed"
+palette = "dark16"
+threshold = 11
+check_contrast = true
+
+[templates]
+hyprland = { template = "hyprland-colors.conf", target = "~/.cache/wallust/hyprland-colors.conf" }
+mako = { template = "mako.conf", target = "~/.config/mako/config" }
+eww = { template = "eww-colors.scss", target = "~/.cache/wallust/eww-colors.scss" }
+ghostty = { template = "ghostty-colors.conf", target = "~/.cache/wallust/ghostty-colors.conf" }
+launcher = { template = "launcher-colors.sh", target = "~/.cache/wallust/launcher-colors.sh" }
+spicetify = { template = "spicetify-colors.ini", target = "~/.config/spicetify/Themes/wallust/color.ini" }
+nvim = { template = "nvim.lua", target = "~/.cache/wallust/nvim.lua" }