Add Arch Wayland dotfiles: sway/waybar/kitty/nvim/zsh, idempotent installer
Auto-adapts sway workspace routing to however many monitors are connected (laptop vs. desktop) instead of hardcoding output names.
This commit is contained in:
+35
@@ -0,0 +1,35 @@
|
||||
# Secrets — never commit tokens, keys, or credentials.
|
||||
*.token
|
||||
*.key
|
||||
*.pem
|
||||
*id_rsa*
|
||||
*id_ed25519*
|
||||
.env
|
||||
.env.*
|
||||
**/secrets/
|
||||
**/*secret*
|
||||
!config/**/secrets*.example
|
||||
|
||||
# 1Password / auth material some machines keep alongside dotfiles.
|
||||
.config/op/
|
||||
op/*.token
|
||||
|
||||
# Editor / tool junk.
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
.netrwhist
|
||||
|
||||
# Shell/session state that must stay per-machine.
|
||||
.zsh_history
|
||||
.zcompdump*
|
||||
.cache/
|
||||
|
||||
# Neovim local/session artifacts.
|
||||
config/nvim/.luarc.json
|
||||
**/undodir/
|
||||
|
||||
# Backups written by install.sh (kept under $HOME, never in-repo, but
|
||||
# ignore defensively in case one is ever created inside the checkout).
|
||||
.dotfiles-backup/
|
||||
@@ -0,0 +1,55 @@
|
||||
# Monochrome Arch Wayland dotfiles
|
||||
|
||||
Modular dotfiles for a Sway desktop on Arch Linux. The installer can install the
|
||||
required packages and creates symlinks back to this repository.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
git clone git@git.noroutes.net:owen/dotfiles.git ~/code/dotfiles
|
||||
cd ~/code/dotfiles
|
||||
./install.sh
|
||||
```
|
||||
|
||||
Useful options:
|
||||
|
||||
```sh
|
||||
./install.sh --no-packages # only create links
|
||||
./install.sh --dry-run # show what would happen
|
||||
./install.sh --help
|
||||
```
|
||||
|
||||
Existing files are never deleted: conflicting paths are moved to
|
||||
`~/.dotfiles-backup/<timestamp>/`. Log out and choose Sway after installation.
|
||||
Use `Alt+Shift+r` to reload Sway and `Alt+d` to open Wofi.
|
||||
|
||||
## Layout
|
||||
|
||||
- `config/sway/conf.d/` contains focused Sway fragments.
|
||||
- `home/.zsh.d/*.zsh` is automatically sourced by `home/.zshrc`.
|
||||
- Neovim settings and mappings live in separate Lua modules.
|
||||
- The shared monochrome palette with blue accents is repeated in the native format each application
|
||||
expects, so no template generator is required at runtime.
|
||||
|
||||
## Single vs. dual monitor
|
||||
|
||||
The same configuration works unmodified on the single-monitor laptop and the
|
||||
dual-monitor desktop:
|
||||
|
||||
- Sway ignores `output`/`workspace ... output` directives that reference a
|
||||
display name which isn't currently connected, so the desktop-specific
|
||||
`DP-3`/`DP-2` positioning in `config/sway/conf.d/10-appearance.conf` is a
|
||||
harmless no-op on the laptop.
|
||||
- `config/sway/scripts/select-workspace.sh` (bound to the workspace keys)
|
||||
doesn't hardcode output names. It ranks whatever outputs are connected
|
||||
left-to-right and maps workspaces 1-10 to the first, 11-20 to the second,
|
||||
and so on — so it adapts automatically to however many monitors are
|
||||
plugged in.
|
||||
|
||||
## Secrets
|
||||
|
||||
Nothing in this repository is or should be a secret. Machine-local
|
||||
credentials (e.g. the 1Password service-account token read by
|
||||
`home/.zshrc`) live outside the repo, in files under `$HOME` that
|
||||
`.gitignore` also excludes defensively should they ever end up in the
|
||||
checkout.
|
||||
@@ -0,0 +1,25 @@
|
||||
foreground #d4d4d4
|
||||
background #000000
|
||||
selection_foreground #ffffff
|
||||
selection_background #2563eb
|
||||
cursor #60a5fa
|
||||
cursor_text_color #000000
|
||||
url_color #7dd3fc
|
||||
active_border_color #60a5fa
|
||||
inactive_border_color #262626
|
||||
color0 #000000
|
||||
color1 #737373
|
||||
color2 #a3a3a3
|
||||
color3 #d4d4d4
|
||||
color4 #60a5fa
|
||||
color5 #737373
|
||||
color6 #93c5fd
|
||||
color7 #d4d4d4
|
||||
color8 #525252
|
||||
color9 #a3a3a3
|
||||
color10 #d4d4d4
|
||||
color11 #e5e5e5
|
||||
color12 #93c5fd
|
||||
color13 #a3a3a3
|
||||
color14 #bfdbfe
|
||||
color15 #ffffff
|
||||
@@ -0,0 +1,17 @@
|
||||
font_family JetBrainsMono Nerd Font
|
||||
font_size 11.0
|
||||
background_opacity 0.85
|
||||
window_padding_width 8
|
||||
confirm_os_window_close 0
|
||||
enable_audio_bell no
|
||||
cursor_shape beam
|
||||
include colors.conf
|
||||
|
||||
map ctrl+shift+enter new_window_with_cwd
|
||||
map ctrl+shift+t new_tab_with_cwd
|
||||
|
||||
# Send stable sequences for Zsh word-wise editing.
|
||||
map ctrl+left send_text all \x1b[1;5D
|
||||
map ctrl+right send_text all \x1b[1;5C
|
||||
map ctrl+backspace send_text all \x1b\x7f
|
||||
map ctrl+delete send_text all \x1b[3;5~
|
||||
@@ -0,0 +1,4 @@
|
||||
require("config.options")
|
||||
require("config.plugins")
|
||||
require("config.keymaps")
|
||||
require("config.autocmds")
|
||||
@@ -0,0 +1,10 @@
|
||||
local group = vim.api.nvim_create_augroup("dotfiles", { clear = true })
|
||||
vim.api.nvim_create_autocmd("TextYankPost", {
|
||||
group = group,
|
||||
callback = function() vim.highlight.on_yank({ timeout = 150 }) end,
|
||||
})
|
||||
vim.api.nvim_create_autocmd("BufWritePre", {
|
||||
group = group,
|
||||
pattern = "*",
|
||||
callback = function() pcall(vim.cmd, [[%s/\s\+$//e]]) end,
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
local map = vim.keymap.set
|
||||
map("n", "<leader>w", "<cmd>write<cr>", { desc = "Save file" })
|
||||
map("n", "<leader>q", "<cmd>quit<cr>", { desc = "Quit" })
|
||||
map("n", "<leader>e", "<cmd>Explore<cr>", { desc = "File explorer" })
|
||||
map("n", "<Esc>", "<cmd>nohlsearch<cr>")
|
||||
map("n", "<C-Left>", "<C-w><Left>")
|
||||
map("n", "<C-Down>", "<C-w><Down>")
|
||||
map("n", "<C-Up>", "<C-w><Up>")
|
||||
map("n", "<C-Right>", "<C-w><Right>")
|
||||
map("v", "J", ":m '>+1<cr>gv=gv")
|
||||
map("v", "K", ":m '<-2<cr>gv=gv")
|
||||
@@ -0,0 +1,31 @@
|
||||
vim.g.mapleader = " "
|
||||
vim.g.maplocalleader = " "
|
||||
|
||||
local opt = vim.opt
|
||||
opt.number = true
|
||||
opt.relativenumber = true
|
||||
opt.cursorline = true
|
||||
opt.termguicolors = true
|
||||
opt.signcolumn = "yes"
|
||||
opt.wrap = false
|
||||
opt.scrolloff = 8
|
||||
opt.sidescrolloff = 8
|
||||
opt.tabstop = 2
|
||||
opt.shiftwidth = 2
|
||||
opt.expandtab = true
|
||||
opt.smartindent = true
|
||||
opt.ignorecase = true
|
||||
opt.smartcase = true
|
||||
opt.splitbelow = true
|
||||
opt.splitright = true
|
||||
opt.undofile = true
|
||||
opt.updatetime = 250
|
||||
opt.timeoutlen = 400
|
||||
|
||||
vim.cmd.colorscheme("habamax")
|
||||
vim.api.nvim_set_hl(0, "Normal", { fg = "#d4d4d4", bg = "#000000" })
|
||||
vim.api.nvim_set_hl(0, "NormalFloat", { fg = "#d4d4d4", bg = "#0a0a0a" })
|
||||
vim.api.nvim_set_hl(0, "CursorLine", { bg = "#0a0a0a" })
|
||||
vim.api.nvim_set_hl(0, "Visual", { bg = "#2563eb" })
|
||||
vim.api.nvim_set_hl(0, "LineNr", { fg = "#525252" })
|
||||
vim.api.nvim_set_hl(0, "CursorLineNr", { fg = "#60a5fa", bold = true })
|
||||
@@ -0,0 +1,10 @@
|
||||
vim.pack.add({
|
||||
"https://github.com/preservim/nerdtree",
|
||||
}, { confirm = false })
|
||||
|
||||
vim.g.NERDTreeWinPos = "left"
|
||||
vim.g.NERDTreeWinSize = 32
|
||||
|
||||
vim.api.nvim_create_user_command("Tree", "NERDTreeToggle", {
|
||||
desc = "Toggle the NERDTree sidebar",
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"plugins": {
|
||||
"nerdtree": {
|
||||
"rev": "690d061b591525890f1471c6675bcb5bdc8cdff9",
|
||||
"src": "https://github.com/preservim/nerdtree"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
font pango:JetBrainsMono Nerd Font 10
|
||||
gaps inner 8
|
||||
gaps outer 4
|
||||
default_border pixel 2
|
||||
default_floating_border pixel 2
|
||||
|
||||
client.focused #3b82f6 #000000 #60a5fa #2563eb #3b82f6
|
||||
client.focused_inactive #171717 #000000 #737373 #171717 #171717
|
||||
client.unfocused #0a0a0a #000000 #525252 #0a0a0a #0a0a0a
|
||||
client.urgent #60a5fa #000000 #60a5fa #3b82f6 #60a5fa
|
||||
|
||||
for_window [app_id="system-monitor"] floating enable, resize set 1000 700, move position center
|
||||
|
||||
output * bg ~/.config/sway/wallpapers/blue-horizon.svg fill
|
||||
|
||||
# Desktop-only dual-monitor arrangement: DP-3 is the centered primary
|
||||
# display, DP-2 sits immediately to its left. Sway silently ignores
|
||||
# `output` directives for names that don't exist, so this is a no-op on
|
||||
# the single-monitor laptop (its internal panel, e.g. eDP-1, is untouched
|
||||
# and simply gets everything). Workspace-to-output routing is not pinned
|
||||
# here; scripts/select-workspace.sh assigns workspaces to whichever
|
||||
# outputs are actually connected, in left-to-right order, so it adapts
|
||||
# automatically on both machines without any per-host config.
|
||||
output DP-3 position 0 0
|
||||
output DP-2 position -2560 0
|
||||
|
||||
output DP-3 scale 1
|
||||
output DP-2 scale 1
|
||||
@@ -0,0 +1,14 @@
|
||||
input type:keyboard {
|
||||
xkb_layout us
|
||||
repeat_delay 300
|
||||
repeat_rate 40
|
||||
}
|
||||
|
||||
input type:touchpad {
|
||||
tap enabled
|
||||
natural_scroll enabled
|
||||
dwt enabled
|
||||
}
|
||||
|
||||
# Make workspace number bindings follow the monitor under the pointer.
|
||||
focus_follows_mouse always
|
||||
@@ -0,0 +1,4 @@
|
||||
exec_always --no-startup-id sh -c 'pkill waybar; exec waybar'
|
||||
exec --no-startup-id mako
|
||||
exec --no-startup-id nm-applet --indicator
|
||||
exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
||||
@@ -0,0 +1,16 @@
|
||||
set $mod Mod1
|
||||
set $term kitty
|
||||
set $menu wofi --show drun
|
||||
|
||||
# Enable Xwayland
|
||||
xwayland enable
|
||||
|
||||
# Random xorg shit
|
||||
exec_always dbus-update-activation-environment --systemd \
|
||||
WAYLAND_DISPLAY DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP XDG_SESSION_TYPE
|
||||
|
||||
exec_always systemctl --user import-environment \
|
||||
WAYLAND_DISPLAY DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP XDG_SESSION_TYPE
|
||||
|
||||
include ~/.config/sway/conf.d/*.conf
|
||||
include ~/.config/sway/keybinds
|
||||
@@ -0,0 +1,60 @@
|
||||
floating_modifier $mod normal
|
||||
bindsym $mod+Return exec $term
|
||||
bindsym $mod+d exec $menu
|
||||
bindsym $mod+Shift+q kill
|
||||
bindsym $mod+Shift+c reload
|
||||
bindsym $mod+Shift+r reload
|
||||
bindsym $mod+Shift+e exec swaynag -t warning -m 'Exit Sway?' -b 'Exit' '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+f fullscreen toggle
|
||||
bindsym $mod+Shift+space floating toggle
|
||||
|
||||
set $workspace_script ~/.config/sway/scripts/select-workspace.sh
|
||||
bindsym $mod+1 exec $workspace_script 1
|
||||
bindsym $mod+2 exec $workspace_script 2
|
||||
bindsym $mod+3 exec $workspace_script 3
|
||||
bindsym $mod+4 exec $workspace_script 4
|
||||
bindsym $mod+5 exec $workspace_script 5
|
||||
bindsym $mod+6 exec $workspace_script 6
|
||||
bindsym $mod+7 exec $workspace_script 7
|
||||
bindsym $mod+8 exec $workspace_script 8
|
||||
bindsym $mod+9 exec $workspace_script 9
|
||||
bindsym $mod+0 exec $workspace_script 10
|
||||
bindsym $mod+Shift+1 exec $workspace_script 1 move
|
||||
bindsym $mod+Shift+2 exec $workspace_script 2 move
|
||||
bindsym $mod+Shift+3 exec $workspace_script 3 move
|
||||
bindsym $mod+Shift+4 exec $workspace_script 4 move
|
||||
bindsym $mod+Shift+5 exec $workspace_script 5 move
|
||||
bindsym $mod+Shift+6 exec $workspace_script 6 move
|
||||
bindsym $mod+Shift+7 exec $workspace_script 7 move
|
||||
bindsym $mod+Shift+8 exec $workspace_script 8 move
|
||||
bindsym $mod+Shift+9 exec $workspace_script 9 move
|
||||
bindsym $mod+Shift+0 exec $workspace_script 10 move
|
||||
|
||||
bindsym $mod+Shift+s exec grim -g "$(slurp)" - | wl-copy --type image/png
|
||||
bindsym Print exec grim -g "$(slurp)" - | wl-copy --type image/png
|
||||
bindsym Shift+Print exec grim "$HOME/Pictures/screenshot-$(date +%F-%H%M%S).png"
|
||||
bindsym XF86AudioRaiseVolume exec wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+
|
||||
bindsym XF86AudioLowerVolume exec wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindsym XF86AudioMute exec wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindsym XF86MonBrightnessUp exec brightnessctl set +5%
|
||||
bindsym XF86MonBrightnessDown exec brightnessctl set 5%-
|
||||
|
||||
mode "resize" {
|
||||
bindsym Left resize shrink width 40px
|
||||
bindsym Down resize grow height 40px
|
||||
bindsym Up resize shrink height 40px
|
||||
bindsym Right resize grow width 40px
|
||||
bindsym Return mode "default"
|
||||
bindsym Escape mode "default"
|
||||
}
|
||||
bindsym $mod+r mode "resize"
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
slot=${1:-}
|
||||
action=${2:-select}
|
||||
|
||||
if [[ ! "$slot" =~ ^([1-9]|10)$ ]]; then
|
||||
printf 'Usage: %s <1-10> [select|move]\n' "${0##*/}" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Outputs are ordered left-to-right by position, not by name, so this works
|
||||
# unmodified whether there is one output (laptop) or several (desktop):
|
||||
# the focused output's rank in that order selects its block of 10 workspaces.
|
||||
outputs=$(swaymsg -t get_outputs -r | jq -c 'sort_by(.rect.x)')
|
||||
index=$(jq -r '[.[] | .focused] | index(true)' <<<"$outputs")
|
||||
if [[ "$index" == null ]]; then
|
||||
printf 'No focused output found\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
workspace=$((slot + index * 10))
|
||||
|
||||
case "$action" in
|
||||
select) swaymsg workspace number "$workspace" >/dev/null ;;
|
||||
move)
|
||||
swaymsg move container to workspace number "$workspace" >/dev/null
|
||||
;;
|
||||
*)
|
||||
printf 'Unknown action: %s\n' "$action" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,42 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2560" height="1440" viewBox="0 0 2560 1440">
|
||||
<rect width="2560" height="1440" fill="#082f68"/>
|
||||
|
||||
<g fill="#dbeafe">
|
||||
<circle cx="118" cy="112" r="1.5" opacity="0.8"/>
|
||||
<circle cx="284" cy="336" r="1" opacity="0.65"/>
|
||||
<circle cx="438" cy="178" r="2" opacity="0.85"/>
|
||||
<circle cx="612" cy="514" r="1.5" opacity="0.7"/>
|
||||
<circle cx="754" cy="92" r="1" opacity="0.75"/>
|
||||
<circle cx="902" cy="284" r="1.5" opacity="0.9"/>
|
||||
<circle cx="1088" cy="438" r="1" opacity="0.65"/>
|
||||
<circle cx="1246" cy="152" r="2" opacity="0.8"/>
|
||||
<circle cx="1414" cy="352" r="1" opacity="0.7"/>
|
||||
<circle cx="1582" cy="86" r="1.5" opacity="0.85"/>
|
||||
<circle cx="1748" cy="486" r="2" opacity="0.7"/>
|
||||
<circle cx="1918" cy="224" r="1" opacity="0.9"/>
|
||||
<circle cx="2086" cy="390" r="1.5" opacity="0.65"/>
|
||||
<circle cx="2268" cy="126" r="2" opacity="0.8"/>
|
||||
<circle cx="2442" cy="548" r="1" opacity="0.75"/>
|
||||
<circle cx="178" cy="746" r="1" opacity="0.7"/>
|
||||
<circle cx="362" cy="1028" r="2" opacity="0.8"/>
|
||||
<circle cx="538" cy="842" r="1.5" opacity="0.65"/>
|
||||
<circle cx="716" cy="1244" r="1" opacity="0.85"/>
|
||||
<circle cx="886" cy="698" r="2" opacity="0.75"/>
|
||||
<circle cx="1056" cy="1116" r="1.5" opacity="0.9"/>
|
||||
<circle cx="1232" cy="908" r="1" opacity="0.65"/>
|
||||
<circle cx="1402" cy="1302" r="2" opacity="0.8"/>
|
||||
<circle cx="1574" cy="772" r="1" opacity="0.7"/>
|
||||
<circle cx="1746" cy="1064" r="1.5" opacity="0.85"/>
|
||||
<circle cx="1912" cy="876" r="2" opacity="0.7"/>
|
||||
<circle cx="2088" cy="1268" r="1" opacity="0.9"/>
|
||||
<circle cx="2260" cy="714" r="1.5" opacity="0.65"/>
|
||||
<circle cx="2434" cy="1148" r="2" opacity="0.8"/>
|
||||
<circle cx="328" cy="612" r="3.5" opacity="0.9"/>
|
||||
<circle cx="676" cy="356" r="4" opacity="0.95"/>
|
||||
<circle cx="1168" cy="682" r="3" opacity="0.85"/>
|
||||
<circle cx="1462" cy="226" r="4.5" opacity="0.9"/>
|
||||
<circle cx="1840" cy="624" r="3.5" opacity="0.95"/>
|
||||
<circle cx="2184" cy="972" r="4" opacity="0.85"/>
|
||||
<circle cx="948" cy="1268" r="3.5" opacity="0.9"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,23 @@
|
||||
daemonize
|
||||
show-failed-attempts
|
||||
clock
|
||||
indicator
|
||||
indicator-radius=100
|
||||
indicator-thickness=8
|
||||
font=JetBrainsMono Nerd Font
|
||||
font-size=24
|
||||
color=000000
|
||||
inside-color=0a0a0add
|
||||
inside-clear-color=2563ebdd
|
||||
inside-ver-color=171717dd
|
||||
inside-wrong-color=171717dd
|
||||
ring-color=60a5faff
|
||||
ring-clear-color=93c5fdff
|
||||
ring-ver-color=38bdf8ff
|
||||
ring-wrong-color=60a5faff
|
||||
key-hl-color=60a5faff
|
||||
text-color=d4d4d4ff
|
||||
text-clear-color=ffffffff
|
||||
text-ver-color=ffffffff
|
||||
text-wrong-color=ffffffff
|
||||
separator-color=00000000
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"layer": "top",
|
||||
"position": "top",
|
||||
"height": 32,
|
||||
"spacing": 6,
|
||||
"modules-left": ["sway/workspaces", "sway/mode"],
|
||||
"modules-center": ["clock"],
|
||||
"modules-right": ["cpu", "memory", "custom/network", "pulseaudio", "backlight", "battery", "tray", "custom/power"],
|
||||
"sway/workspaces": {
|
||||
"disable-scroll": false,
|
||||
"all-outputs": false,
|
||||
"on-click-right": "~/.config/waybar/scripts/rename-workspace.sh"
|
||||
},
|
||||
"clock": { "format": "{:%a %d %b %H:%M}", "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>" },
|
||||
"cpu": { "format": "CPU {usage}%", "tooltip": true, "on-click": "kitty --class system-monitor --title 'System Monitor' btop" },
|
||||
"memory": { "format": "RAM {percentage}%", "tooltip-format": "{used:0.1f} GiB used", "on-click": "kitty --class system-monitor --title 'System Monitor' btop" },
|
||||
"custom/network": { "exec": "~/.config/waybar/scripts/network-cycle.sh", "return-type": "json", "interval": 300, "signal": 8, "on-click": "~/.config/waybar/scripts/network-cycle.sh next", "on-click-right": "~/.config/waybar/scripts/network-cycle.sh copy" },
|
||||
"pulseaudio": { "format": "{icon} {volume}%", "format-muted": " muted", "format-icons": ["", "", ""], "tooltip-format": "{desc}", "on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", "on-click-right": "~/.config/waybar/scripts/audio-output-cycle.sh" },
|
||||
"backlight": { "format": " {percent}%" },
|
||||
"battery": { "states": { "warning": 30, "critical": 15 }, "format": "{icon} {capacity}%", "format-charging": " {capacity}%", "format-icons": ["", "", "", "", ""] },
|
||||
"tray": { "spacing": 8 },
|
||||
"custom/power": {
|
||||
"format": "",
|
||||
"tooltip": true,
|
||||
"tooltip-format": "Power menu",
|
||||
"on-click": "~/.config/waybar/scripts/power-menu.sh"
|
||||
}
|
||||
}
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
source_ids=()
|
||||
current_index=-1
|
||||
|
||||
while IFS=$'\t' read -r id is_default; do
|
||||
[[ -n "$id" ]] || continue
|
||||
source_ids+=("$id")
|
||||
[[ "$is_default" == true ]] && current_index=$((${#source_ids[@]} - 1))
|
||||
done < <(
|
||||
wpctl status -n | awk '
|
||||
/^Audio$/ {in_audio = 1; next}
|
||||
/^Video$/ {in_audio = 0; in_sources = 0}
|
||||
in_audio && /Sources:/ {in_sources = 1; next}
|
||||
/Filters:/ {in_sources = 0}
|
||||
in_audio && in_sources && match($0, /[0-9]+\./) {
|
||||
id = substr($0, RSTART, RLENGTH - 1)
|
||||
print id "\t" (index($0, "*") ? "true" : "false")
|
||||
}
|
||||
'
|
||||
)
|
||||
|
||||
((${#source_ids[@]} > 0)) || exit 0
|
||||
next_index=$(((current_index + 1) % ${#source_ids[@]}))
|
||||
next_source_id="${source_ids[$next_index]}"
|
||||
wpctl set-default "$next_source_id"
|
||||
|
||||
source_name=$(
|
||||
wpctl inspect "$next_source_id" \
|
||||
| awk -F' = ' '/node.nick/ {gsub(/^"|"$/, "", $2); print $2; exit}'
|
||||
)
|
||||
[[ -n "$source_name" ]] || source_name="Input $next_source_id"
|
||||
notify-send -a Waybar -u low -r 9102 "Audio input changed" "$source_name"
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
sink_ids=()
|
||||
current_index=-1
|
||||
|
||||
while IFS=$'\t' read -r id is_default; do
|
||||
[[ -n "$id" ]] || continue
|
||||
sink_ids+=("$id")
|
||||
[[ "$is_default" == true ]] && current_index=$((${#sink_ids[@]} - 1))
|
||||
done < <(
|
||||
wpctl status -n | awk '
|
||||
/^Audio$/ {in_audio = 1; next}
|
||||
/^Video$/ {in_audio = 0; in_sinks = 0}
|
||||
in_audio && /Sinks:/ {in_sinks = 1; next}
|
||||
/Sources:/ {in_sinks = 0}
|
||||
in_audio && in_sinks && match($0, /[0-9]+\./) {
|
||||
id = substr($0, RSTART, RLENGTH - 1)
|
||||
print id "\t" (index($0, "*") ? "true" : "false")
|
||||
}
|
||||
'
|
||||
)
|
||||
|
||||
((${#sink_ids[@]} > 0)) || exit 0
|
||||
next_index=$(((current_index + 1) % ${#sink_ids[@]}))
|
||||
next_sink_id="${sink_ids[$next_index]}"
|
||||
wpctl set-default "$next_sink_id"
|
||||
|
||||
# Move existing playback streams too; set-default alone only affects new ones.
|
||||
while read -r stream_id; do
|
||||
[[ -n "$stream_id" ]] || continue
|
||||
if wpctl inspect "$stream_id" 2>/dev/null | grep -q 'media.class = "Stream/Output/Audio"'; then
|
||||
wpctl set-target "$stream_id" "$next_sink_id" 2>/dev/null || true
|
||||
fi
|
||||
done < <(
|
||||
wpctl status -n | awk '
|
||||
/^Audio$/ {in_audio = 1; next}
|
||||
/^Video$/ {in_audio = 0; in_streams = 0}
|
||||
in_audio && /Streams:/ {in_streams = 1; next}
|
||||
in_audio && in_streams && match($0, /[0-9]+\./) {
|
||||
print substr($0, RSTART, RLENGTH - 1)
|
||||
}
|
||||
'
|
||||
)
|
||||
|
||||
sink_name=$(
|
||||
wpctl inspect "$next_sink_id" \
|
||||
| awk -F' = ' '/node.nick/ {gsub(/^"|"$/, "", $2); print $2; exit}'
|
||||
)
|
||||
[[ -n "$sink_name" ]] || sink_name="Output $next_sink_id"
|
||||
notify-send -a Waybar -u low -r 9102 "Audio output changed" "$sink_name"
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
state_file="${XDG_RUNTIME_DIR:-/tmp}/waybar-network-state-${UID}"
|
||||
current_ip_file="${XDG_RUNTIME_DIR:-/tmp}/waybar-network-current-ip-${UID}"
|
||||
|
||||
if [[ "${1:-}" == copy ]]; then
|
||||
if [[ -s "$current_ip_file" ]]; then
|
||||
current_ip=$(<"$current_ip_file")
|
||||
printf '%s' "$current_ip" | wl-copy
|
||||
notify-send -a Waybar -u low -r 9101 "IP address copied" "$current_ip"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${1:-}" == next ]]; then
|
||||
state=0
|
||||
[[ -r "$state_file" ]] && read -r state <"$state_file"
|
||||
printf '%s\n' "$((state + 1))" >"$state_file"
|
||||
pkill -RTMIN+8 waybar 2>/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
entries=()
|
||||
while read -r interface addresses; do
|
||||
[[ -n "$interface" && -n "$addresses" ]] || continue
|
||||
entries+=("$interface $addresses")
|
||||
done < <(
|
||||
ip -o -4 addr show scope global \
|
||||
| awk '{sub(/\/.*/, "", $4); addresses[$2] = addresses[$2] (addresses[$2] ? "," : "") $4} END {for (interface in addresses) print interface, addresses[interface]}' \
|
||||
| sort
|
||||
)
|
||||
|
||||
public_ipv4=$(curl -4 -fsS --max-time 3 https://ifconfig.me/ip 2>/dev/null || true)
|
||||
[[ -n "$public_ipv4" ]] && entries+=("public $public_ipv4")
|
||||
|
||||
if ((${#entries[@]} == 0)); then
|
||||
entries+=("network unavailable")
|
||||
fi
|
||||
|
||||
state=0
|
||||
[[ -r "$state_file" ]] && read -r state <"$state_file"
|
||||
index=$((state % ${#entries[@]}))
|
||||
tooltip=$(printf '%s\n' "${entries[@]}")
|
||||
current_ip="${entries[$index]#* }"
|
||||
if [[ "$current_ip" == unavailable ]]; then
|
||||
: >"$current_ip_file"
|
||||
else
|
||||
printf '%s' "$current_ip" >"$current_ip_file"
|
||||
fi
|
||||
|
||||
jq -cn --arg text " ${entries[$index]}" --arg tooltip "$tooltip" \
|
||||
'{text: $text, tooltip: $tooltip}'
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
choice=$(
|
||||
printf '%s\n' 'Lock' 'Logout' 'Suspend' 'Reboot' 'Shut down' \
|
||||
| wofi --dmenu --prompt 'Power' --width 320 --height 300
|
||||
) || exit 0
|
||||
|
||||
case "$choice" in
|
||||
Lock) swaylock -f ;;
|
||||
Logout) swaymsg exit ;;
|
||||
Suspend) systemctl suspend ;;
|
||||
Reboot) systemctl reboot ;;
|
||||
'Shut down') systemctl poweroff ;;
|
||||
esac
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
workspace=$(swaymsg -t get_workspaces -r | jq -c '.[] | select(.focused)')
|
||||
number=$(jq -r '.num' <<<"$workspace")
|
||||
current_name=$(jq -r '.name' <<<"$workspace")
|
||||
|
||||
if (( number < 1 )); then
|
||||
notify-send -a Waybar -u low "Workspace rename" \
|
||||
"The focused workspace does not have a numeric identity."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_label=${current_name#"$number:"}
|
||||
if [[ "$current_label" == "$current_name" ]]; then
|
||||
current_label=
|
||||
else
|
||||
current_label=${current_label# }
|
||||
fi
|
||||
|
||||
label=$(printf '%s\n' "$current_label" | wofi --dmenu \
|
||||
--prompt "Name workspace $number (blank resets)" \
|
||||
--lines 1) || exit 0
|
||||
|
||||
label=${label//$'\n'/ }
|
||||
if [[ -z "${label//[[:space:]]/}" ]]; then
|
||||
new_name=$number
|
||||
else
|
||||
new_name="$number: $label"
|
||||
fi
|
||||
|
||||
quoted_name=$(jq -Rrn --arg name "$new_name" '$name | @json')
|
||||
swaymsg "rename workspace to $quoted_name" >/dev/null
|
||||
@@ -0,0 +1,20 @@
|
||||
@define-color bg #000000;
|
||||
@define-color surface #0a0a0a;
|
||||
@define-color blue #60a5fa;
|
||||
@define-color blue-dark #2563eb;
|
||||
@define-color text #d4d4d4;
|
||||
@define-color muted #737373;
|
||||
|
||||
* { border: none; border-radius: 0; font-family: "JetBrainsMono Nerd Font"; font-size: 13px; min-height: 0; }
|
||||
window#waybar { background: @bg; color: @text; }
|
||||
#workspaces button { padding: 0 10px; margin: 4px 2px; color: @muted; border-radius: 6px; }
|
||||
#workspaces button:hover { background: @surface; background-image: none; color: @text; border: none; box-shadow: none; text-shadow: none; }
|
||||
#workspaces button.focused { color: @bg; background: @blue; }
|
||||
#workspaces button.focused:hover { background: @blue; color: @bg; }
|
||||
#workspaces button.urgent { background: @blue-dark; color: @text; }
|
||||
#mode, #clock, #cpu, #memory, #custom-network, #pulseaudio, #backlight, #battery, #tray, #custom-power { padding: 0 10px; margin: 4px 2px; background: transparent; border-radius: 6px; }
|
||||
#mode:hover, #clock:hover, #cpu:hover, #memory:hover, #custom-network:hover, #pulseaudio:hover, #backlight:hover, #battery:hover, #tray:hover, #custom-power:hover { background: @surface; }
|
||||
#clock { color: @blue; font-weight: bold; background: transparent; }
|
||||
#cpu, #memory { color: @text; }
|
||||
#custom-power { color: @blue; font-size: 16px; }
|
||||
#battery.warning, #battery.critical { color: @blue; }
|
||||
@@ -0,0 +1,9 @@
|
||||
show=drun
|
||||
width=560
|
||||
height=400
|
||||
location=center
|
||||
allow_images=true
|
||||
insensitive=true
|
||||
prompt=Search
|
||||
term=kitty
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
* { font-family: "JetBrainsMono Nerd Font"; font-size: 14px; }
|
||||
window { background-color: #000000; color: #d4d4d4; border: 2px solid #3b82f6; border-radius: 10px; }
|
||||
#input { margin: 12px; padding: 10px; color: #60a5fa; background: #0a0a0a; border: 1px solid #2563eb; border-radius: 7px; }
|
||||
#inner-box { margin: 0 12px 12px; }
|
||||
#entry { padding: 8px; border-radius: 6px; }
|
||||
#entry:selected { background: #2563eb; color: #ffffff; }
|
||||
#img { margin-right: 10px; }
|
||||
@@ -0,0 +1,6 @@
|
||||
export EDITOR=nvim
|
||||
export VISUAL=nvim
|
||||
export PAGER=less
|
||||
export LESS='-R'
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
setopt auto_cd auto_pushd pushd_ignore_dups
|
||||
setopt hist_ignore_all_dups hist_reduce_blanks hist_save_no_dups share_history
|
||||
setopt interactive_comments extended_glob
|
||||
HISTFILE="$HOME/.zsh_history"
|
||||
HISTSIZE=50000
|
||||
SAVEHIST=10000
|
||||
|
||||
[[ -d "$HOME/.cache/zsh" ]] || mkdir -p "$HOME/.cache/zsh"
|
||||
autoload -Uz compinit && compinit -d "$HOME/.cache/zsh/zcompdump"
|
||||
zstyle ':completion:*' menu select
|
||||
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'
|
||||
@@ -0,0 +1,16 @@
|
||||
bindkey -e
|
||||
bindkey '^[[H' beginning-of-line
|
||||
bindkey '^[[F' end-of-line
|
||||
bindkey '^[[3~' delete-char
|
||||
bindkey '^R' history-incremental-search-backward
|
||||
|
||||
# Word-wise editing. The CSI bindings are emitted by Kitty (and many other
|
||||
# terminals); the Meta bindings keep the same actions working elsewhere.
|
||||
bindkey '^[b' backward-word
|
||||
bindkey '^[f' forward-word
|
||||
bindkey '^[[1;5D' backward-word
|
||||
bindkey '^[[1;5C' forward-word
|
||||
|
||||
bindkey '^[^?' backward-kill-word
|
||||
bindkey '^[[127;5u' backward-kill-word
|
||||
bindkey '^[[3;5~' kill-word
|
||||
@@ -0,0 +1,16 @@
|
||||
alias ls='ls --color=auto --group-directories-first'
|
||||
alias ll='ls -lah'
|
||||
alias la='ls -A'
|
||||
alias grep='grep --color=auto'
|
||||
alias vim='nvim'
|
||||
alias v='nvim'
|
||||
alias sway-reload='swaymsg reload'
|
||||
alias pkginstall='yay -Syu'
|
||||
alias pkgsearch='yay -Ss'
|
||||
alias pkgremove='yay -Rns'
|
||||
alias pkgupdate='yay -Syu'
|
||||
|
||||
mkcd() { mkdir -p -- "$1" && cd -- "$1"; }
|
||||
|
||||
# Keep Kitty's terminfo locally, but advertise a widely supported type over SSH.
|
||||
ssh() { TERM=xterm-256color command ssh "$@"; }
|
||||
@@ -0,0 +1,5 @@
|
||||
autoload -Uz colors && colors
|
||||
setopt prompt_subst
|
||||
PROMPT='%F{blue}%n%f@%F{cyan}%m%f %F{blue}%~%f %(?.%F{green}.%F{red})❯%f '
|
||||
RPROMPT='%F{240}%D{%H:%M}%f'
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
export ZDOTDIR="${ZDOTDIR:-$HOME}"
|
||||
export ZSH_CONFIG_DIR="$HOME/.zsh.d"
|
||||
|
||||
setopt null_glob
|
||||
for zsh_config in "$ZSH_CONFIG_DIR"/*.zsh; do
|
||||
source "$zsh_config"
|
||||
done
|
||||
unset zsh_config
|
||||
|
||||
|
||||
# opencode
|
||||
export PATH=/home/owen/.opencode/bin:$PATH
|
||||
export PATH="$HOME/.npm-global/bin:$PATH"
|
||||
|
||||
# 1Password service account (scoped to Homelab vault) — headless CLI auth, no biometric prompts
|
||||
export OP_SERVICE_ACCOUNT_TOKEN="$(cat ~/.config/op/homelab-agent.token 2>/dev/null)"
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
DOTFILES_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BACKUP_ROOT="${HOME}/.dotfiles-backup/$(date +%Y%m%d-%H%M%S)"
|
||||
INSTALL_PACKAGES=true
|
||||
DRY_RUN=false
|
||||
|
||||
PACKAGES=(
|
||||
sway swaybg swaylock waybar wofi kitty jq curl btop
|
||||
greetd greetd-tuigreet
|
||||
zsh neovim git base-devel
|
||||
grim slurp wl-clipboard mako libnotify brightnessctl playerctl
|
||||
pipewire pipewire-pulse wireplumber pavucontrol
|
||||
network-manager-applet polkit-gnome xdg-desktop-portal-wlr
|
||||
noto-fonts noto-fonts-emoji ttf-jetbrains-mono-nerd
|
||||
)
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: ./install.sh [--no-packages] [--dry-run] [--help]
|
||||
|
||||
--no-packages Skip pacman package installation
|
||||
--dry-run Print actions without changing anything
|
||||
--help Show this help
|
||||
EOF
|
||||
}
|
||||
|
||||
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
||||
run() {
|
||||
if "$DRY_RUN"; then printf ' '; printf '%q ' "$@"; printf '\n';
|
||||
else "$@"; fi
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--no-packages) INSTALL_PACKAGES=false ;;
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) printf 'Unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
link_path() {
|
||||
local source=$1 target=$2 relative backup
|
||||
if [[ -L "$target" && "$(readlink -f -- "$target")" == "$(readlink -f -- "$source")" ]]; then
|
||||
log "Already linked: $target"
|
||||
return
|
||||
fi
|
||||
if [[ -e "$target" || -L "$target" ]]; then
|
||||
relative=${target#"${HOME}/"}
|
||||
backup="${BACKUP_ROOT}/${relative}"
|
||||
log "Backing up $target -> $backup"
|
||||
run mkdir -p -- "$(dirname -- "$backup")"
|
||||
run mv -- "$target" "$backup"
|
||||
fi
|
||||
run mkdir -p -- "$(dirname -- "$target")"
|
||||
log "Linking $target -> $source"
|
||||
run ln -s -- "$source" "$target"
|
||||
}
|
||||
|
||||
install_system_file() {
|
||||
local source=$1 target=$2 relative backup
|
||||
if [[ -r "$target" ]] && cmp -s -- "$source" "$target"; then
|
||||
log "Already installed: $target"
|
||||
return
|
||||
fi
|
||||
if [[ -e "$target" ]]; then
|
||||
relative=${target#/}
|
||||
backup="${BACKUP_ROOT}/${relative}"
|
||||
log "Backing up $target -> $backup"
|
||||
run mkdir -p -- "$(dirname -- "$backup")"
|
||||
run sudo cp -- "$target" "$backup"
|
||||
fi
|
||||
log "Installing $target"
|
||||
run sudo install -Dm644 -- "$source" "$target"
|
||||
}
|
||||
|
||||
if "$INSTALL_PACKAGES"; then
|
||||
command -v pacman >/dev/null || { echo 'pacman was not found; use --no-packages on non-Arch systems.' >&2; exit 1; }
|
||||
log "Installing Arch packages"
|
||||
run sudo pacman -Syu --needed "${PACKAGES[@]}"
|
||||
|
||||
if ! command -v yay >/dev/null; then
|
||||
log "Installing yay from the AUR"
|
||||
if "$DRY_RUN"; then
|
||||
printf ' git clone https://aur.archlinux.org/yay.git <temporary-directory>/yay\n'
|
||||
printf ' cd <temporary-directory>/yay && makepkg -si --noconfirm\n'
|
||||
else
|
||||
YAY_BUILD_DIR=$(mktemp -d)
|
||||
git clone https://aur.archlinux.org/yay.git "$YAY_BUILD_DIR/yay"
|
||||
(
|
||||
cd "$YAY_BUILD_DIR/yay"
|
||||
makepkg -si --noconfirm
|
||||
)
|
||||
rm -rf -- "$YAY_BUILD_DIR"
|
||||
unset YAY_BUILD_DIR
|
||||
fi
|
||||
fi
|
||||
|
||||
install_system_file "$DOTFILES_DIR/system/greetd/config.toml" /etc/greetd/config.toml
|
||||
log "Enabling greetd"
|
||||
run sudo systemctl enable greetd.service
|
||||
fi
|
||||
|
||||
for source in "$DOTFILES_DIR"/config/*; do
|
||||
[[ -e "$source" ]] || continue
|
||||
link_path "$source" "$HOME/.config/$(basename -- "$source")"
|
||||
done
|
||||
for source in "$DOTFILES_DIR"/home/.[!.]* "$DOTFILES_DIR"/home/..?*; do
|
||||
[[ -e "$source" ]] || continue
|
||||
link_path "$source" "$HOME/$(basename -- "$source")"
|
||||
done
|
||||
|
||||
if [[ "${SHELL:-}" != */zsh ]]; then
|
||||
log "Zsh is installed. Run 'chsh -s /bin/zsh' to make it your login shell."
|
||||
fi
|
||||
log "Installation complete. Reload Sway with Alt+Shift+r."
|
||||
@@ -0,0 +1,6 @@
|
||||
[terminal]
|
||||
vt = 1
|
||||
|
||||
[default_session]
|
||||
command = "tuigreet --time --remember --remember-session --asterisks --cmd sway"
|
||||
user = "greeter"
|
||||
Reference in New Issue
Block a user