Compare commits

...

26 Commits

Author SHA1 Message Date
dissimulo 48814c5d42 tmux-net: trailing space after the ✕ offline glyph for status-bar padding 2026-06-30 16:57:22 -07:00
dissimulo 0eb605a835 tmux statusline: directly-held WAN/LAN IP, offline ✕, native window-status
- tmux-ip: show the first directly-held public/WAN IP on a non-tunnel interface,
  else the LAN/private IP. No external 'what's my IP' lookup and no DigitalOcean
  special-case (a droplet's eth0 public IP is found by the same interface scan);
  RFC1918/link-local/CGNAT classed as not-WAN, so a NAT'd box shows its LAN IP.
- tmux-net: show ✕ when there's no local IP and no VPN tunnel (offline). VPN ⇡
  overlay unchanged.
- .tmux.conf: window-status icon + path now native tmux formats (no per-redraw
  #() forks); tmux-window-icon kept as fallback.
2026-06-30 16:39:52 -07:00
dissimulo c32dbb92ac claude: fullscreen tui 2026-06-30 16:32:46 -07:00
dissimulo 8c3dcacafc claude: dark-daltonized theme 2026-06-30 15:23:32 -07:00
dissimulo c680be4817 lolcat: revert to busyloop (Debian) active; jaseg removed from PATH (too coarse) 2026-06-24 09:40:27 -07:00
dissimulo 13f43c3c5f lolcat: jaseg C build now active at /usr/local/bin (ahead of busyloop) 2026-06-24 09:35:35 -07:00
dissimulo aafdc3a119 lolcat: correct attribution to busyloop Ruby build (Debian pkg upstream) 2026-06-24 09:29:33 -07:00
dissimulo 4053acbbc0 Add zsh/lolcat.zsh rainbow helpers + document in README 2026-06-24 09:12:17 -07:00
dissimulo 2906352220 tmux: lowercase am/pm in statusline clock 2026-06-16 17:12:10 -07:00
dissimulo 1d2a823750 tmux: drop space between time and AM/PM in statusline 2026-06-16 17:11:44 -07:00
dissimulo 8400ddf6a8 tmux: 12-hour clock in statusline (%-I:%M %p) 2026-06-16 17:08:27 -07:00
dissimulo 11bf7ecaa4 zsh: eagerly define diff() to avoid prezto autoload-stub error
Pretzo's modules/utility/functions/diff registers an autoload stub that
emits '(eval):1: diff: function definition file not found' in non-
interactive eval contexts where $fpath doesn't include the utility
module's functions directory. Define diff as a real function up front so
the stub never has to look itself up.
2026-05-17 04:02:13 -07:00
dissimulo 3d6f13b2bc tmux-net: always prefix local IP with home symbol
Show ⌂ before the local IP whether or not a VPN is up. The VPN branch
already prefixes ⌂; the non-VPN branch was emitting the bare IP.
2026-05-17 03:58:11 -07:00
dissimulo e2a1e3b784 tmux status: use short hostname (#h) instead of FQDN (#H) 2026-05-17 03:23:55 -07:00
dissimulo e38a6b3166 tmux status: show VPN IP when tunnel is active
Add tmux-vpn-ip helper (parses ifconfig for utun* on macOS, falls back
to tun/wg/ppp on Linux) and a tmux-net wrapper that conditionally
emits "⌂ <local> / ⇡ <vpn>" when a VPN tunnel is up, or just "<local>"
when it's not. status-right now calls tmux-net.
2026-05-17 03:19:26 -07:00
dissimulo 467b967ced tmux-ip: portable local-IP fallback for macOS
`hostname -I` is GNU-only and errors on BSD/macOS, so when the public-IP
curl couldn't reach api.ipify.org the script printed nothing and the
tmux status bar showed an empty IP. Try `ipconfig getifaddr` for common
interfaces first, fall back to `hostname -I` on Linux, then `ifconfig`
as a last resort. Also guard against empty-but-zero-exit curl output.
2026-05-17 03:12:11 -07:00
dissimulo 474fdd028c Rakefile: handle non-interactive stdin in ask()
STDIN.gets returns nil when stdin is closed/EOF (e.g. after vim/vundle
runs during install), causing `nil.chomp` to abort rake install during
install_term_theme. Return nil from ask() on EOF and have callers bail
out of the iTerm theme prompts gracefully.
2026-05-17 02:14:29 -07:00
dissimulo a45f89b187 .claude/settings.json: prune server-specific entries; consolidate redundant ones
The previous snapshot of settings.json was a verbatim dump from one
Mastodon-server install — it carried allowlist entries that won't
match anywhere else and a lot of narrow rules already subsumed by
broader wildcards.

Removed (server-specific, dead weight on other hosts):
- /home/mastodon/* paths and Mastodon .env.production sed/chmod/chown
- signers.online and auto.signers.online curl/openssl probes
- mastodon-web / mastodon-streaming / mastodon-sidekiq journalctl
- n8n journalctl, windmill journalctl + binary
- /usr/local/bin/fail2ban-ignoreip and the hardcoded IP 76.95.82.63
- nslookup signers.live, nginx site-availables grep with literal paths
- /var/log/nginx/access.log* zcat probes (path-specific)
- StatusReactions / status_quoted grep over Mastodon's frontend tree

Removed (redundant, covered by broader wildcard already in the list):
- All narrow Bash(systemctl <verb>:*) entries — Bash(systemctl:*) covers
- All narrow Bash(git ...) entries — Bash(git:*) covers
- All narrow Bash(curl ...) probes — Bash(curl:*) covers
- Bash(rkhunter --update), Bash(rkhunter --propupd) — Bash(rkhunter:*) covers
- Bash(sysctl -a) — Bash(sysctl:*) covers

Kept: tmux/git/curl/sudo/find/ls/cat plus generic system-admin verbs
(systemctl, sysctl, crontab, iptables, ufw, firewall-cmd, fail2ban-client,
apt/apt-get/dpkg, mount, netstat, openssl, lsmod, last, nginx, redis-cli,
rkhunter, aideinit, getent, sqlite3, dig, ulimit, getenforce, aa-status)
plus Read(//home/**), Read(//opt/**), Read(//etc/nginx/sites-{enabled,available}/**).

Net: 5004 -> 1434 bytes (57 lines), still valid JSON, behavior on a
fresh host is identical for the kept verbs and tighter for the dropped
ones (host-specific allows just won't match anything anyway).

If you re-run install.sh on this host, the slim version replaces the
fat one; the fat one survives in ~/.drunkendotfiles.bak.<ts>/ for
recovery.
2026-05-06 10:10:21 +00:00
dissimulo 1b60a9364d Add portable Claude Code config
Saves ~/.claude/settings.json (user-level prefs + Bash/Read permissions
allowlist) so the same Claude Code config can be replicated on other
hosts via install.sh.

What gets deployed:
- .claude/settings.json — theme, verbose, defaultMode, skipAutoPermissionPrompt
  and the cumulative "approve once" allowlist for common ops (systemctl,
  iptables, journalctl, git, curl, etc.). Server-specific allow entries
  (e.g. /home/mastodon/* paths) are dead weight elsewhere but harmless.
- .claude/.gitignore — explicit deny-list so a future `git add .claude`
  doesn't accidentally pull in credentials, session logs, project
  memories, file-history, telemetry, caches, or settings.local.json.

What is NOT tracked (by design):
- .credentials.json (auth)
- history.jsonl, sessions/, projects/ (chat data, project memories)
- settings.local.json (per-machine overrides — by Claude Code convention)
- file-history/, plans/, paste-cache/, shell-snapshots/, session-env/,
  telemetry/, downloads/, cache/, backups/, mcp-needs-auth-cache.json
- plugins/ (marketplace install paths are absolute and host-specific)

deploy_dir's merge semantics mean: on a host that already has
~/.claude/.credentials.json or ~/.claude/projects/, those stay untouched
because the repo doesn't track them. Only the files we explicitly
include get installed.

Existing settings.json on the target host is moved aside to
~/.drunkendotfiles.bak.<timestamp>/ before being replaced.
2026-05-06 10:06:36 +00:00
dissimulo 5208e9a5bb install.sh: repoint dangling claude symlink to latest installed version
The committed ~/.local/bin/claude symlink targets a specific Claude Code
version path (whatever was current when this snapshot was taken). On any
host that has Claude Code installed at a different version, that symlink
arrives dangling.

Old behavior: just delete the dangling link and rely on the Claude
installer to recreate it. That doesn't help when the host has Claude
installed but at a version other than the one in the snapshot, and we
also support skipping the installer via DRUNKENDOTFILES_SKIP_CLAUDE=1
(used when the binary is already on disk but at a different version).

New behavior: when the deployed symlink is dangling, scan
~/.local/share/claude/versions/ and repoint the symlink to the highest
installed version (semver sort). Only fall back to deleting the link if
no version is installed at all.
2026-05-06 09:45:52 +00:00
dissimulo 6188583ee9 tmux: enable truecolor + focus-events for TUI apps
- Switch default-terminal from screen-256color to tmux-256color
  (better italics/undercurl/SGR support for nvim, Claude Code, etc.)
- Add terminal-features RGB for the common outer terminals so
  truecolor escape sequences pass through cleanly
- Enable focus-events so inner programs can redraw when the pane
  regains focus
2026-05-06 09:37:20 +00:00
dissimulo f27ba2710e Pin git identity to dissimulo / connect+gitea@dustin-williams.com
Identity was previously deferred to ~/.gitconfig.user via the include
at the bottom of this file. Per the dotfiles owner, identity should
travel with the dotfiles regardless of host or local user — every
machine where this repo is deployed commits as dissimulo.

Per-machine bits (safe.directory, etc.) still belong in
~/.gitconfig.user, which the existing [include] at the bottom picks
up after this block.
2026-05-06 02:26:08 -07:00
dissimulo c0e4a88d70 Add ~/.local/bin to PATH from prezto-override zshrc
The Claude Code installer drops the binary at ~/.local/bin/claude.
Without this PATH entry, freshly-installed Claude Code is invisible to
new shells until the user adds it manually. Other ~/.local/bin scripts
in this dotfiles repo (tmux-ip, tmux-window-icon, twitterbot, etc.)
benefit from the same.
2026-05-06 02:24:33 -07:00
dissimulo 700da3aa0b Make tmux/tmux.conf a relative symlink to ../.tmux.conf
YADR's Rakefile maps tmux/tmux.conf -> $HOME/.tmux.conf, but the
canonical, customized tmux config has lived at the repo root since
3d2508a (when the relationship was inverted). Re-running rake install
on its own (without the bash installer's subsequent personal-deploy
overlay) was therefore replacing the deployed real .tmux.conf with a
symlink to the older 3987-byte tmux/tmux.conf, dropping the IP/host
status-right and the bumped history limit.

Pointing tmux/tmux.conf at ../.tmux.conf via a relative symlink fixes
this on any machine, regardless of where ~/.yadr lives or which user
owns it: rake install's symlink chain now resolves to the customized
config no matter the install order.
2026-05-06 02:24:32 -07:00
dissimulo fb80b6dfb2 gitconfig: use absolute include path for .gitconfig.user
The previous `[include] path = .gitconfig.user` was resolved relative to
the config file being read, which — because `~/.gitconfig` is a symlink
to `~/.yadr/git/gitconfig` — meant git looked for the user-identity
override at `~/.yadr/git/.gitconfig.user` instead of the conventional
`~/.gitconfig.user`. Switch to an absolute path so the include picks up
the homedir file regardless of symlink layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 07:41:12 -07:00
dissimulo 1d7df94a1d install.sh: merge PERSONAL_DIRS contents instead of wholesale replace
The previous deploy_one() did `mv $HOME/<dir> $BACKUP_DIR/<dir>` then
`cp -a $YADR_DIR/<dir> $HOME/<dir>` for every entry in PERSONAL_DIRS.
For dirs the repo only partially populates (notably .local — repo only
tracks .local/bin/), this swept away unrelated user data: the most
recent re-bootstrap moved ~/.local/share/fonts/ (Intel One Mono, Open
Gorton, Roboto Mono, GALLAUDET, code128) into the timestamped backup,
making them appear missing.

Rework deploy logic:
- deploy_file: copies one file/symlink, backing up only the conflicting
  destination (if any). Idempotent via paths_equivalent() so re-runs
  with no changes produce no output and no spurious backups.
- deploy_dir: walks the repo's tree for that dir and deploys each leaf
  via deploy_file. Files in $HOME the repo doesn't know about are left
  untouched. Subdirs are mkdir'd as needed.

Also: track the personal fonts at .local/share/fonts/ so they redeploy
on every install, and run fc-cache -f at the end so apps see them
without a logout/login.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 07:28:40 -07:00
28 changed files with 407 additions and 149 deletions
+33
View File
@@ -0,0 +1,33 @@
# Never track Claude Code per-machine state, secrets, or chat data.
#
# settings.json IS tracked (portable user prefs + permissions allowlist).
# Everything else under ~/.claude/ stays per-host: credentials, session
# logs, project memories, file-edit history, telemetry, caches.
# Auth — must NEVER be tracked
.credentials.json
# Per-machine settings overrides (by Claude Code convention)
settings.local.json
# Chat and session data
history.jsonl
sessions/
projects/
file-history/
plans/
# Caches and ephemeral state
cache/
downloads/
paste-cache/
shell-snapshots/
session-env/
backups/
telemetry/
mcp-needs-auth-cache.json
# Plugin marketplace state — known_marketplaces.json IS portable, but the
# resolved-plugin caches and the local blocklist are per-machine.
plugins/blocklist.json
plugins/marketplaces/
+58
View File
@@ -0,0 +1,58 @@
{
"permissions": {
"allow": [
"Bash(tmux source-file:*)",
"Bash(git:*)",
"Bash(curl:*)",
"Bash(sudo:*)",
"Bash(find:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(systemctl:*)",
"Bash(sysctl:*)",
"Bash(crontab:*)",
"Bash(dig:*)",
"Bash(ulimit:*)",
"Bash(python3:*)",
"Bash(iptables:*)",
"Bash(ip6tables:*)",
"Bash(ufw status:*)",
"Bash(firewall-cmd:*)",
"Bash(apt list:*)",
"Bash(apt-get install:*)",
"Bash(apt-get upgrade:*)",
"Bash(dpkg:*)",
"Bash(fail2ban-client status:*)",
"Bash(fail2ban-client set:*)",
"Bash(aa-status)",
"Bash(getenforce)",
"Bash(mount)",
"Bash(netstat -tuln)",
"Bash(netstat -tlnp)",
"Bash(openssl x509:*)",
"Bash(openssl rand:*)",
"Bash(grep -v \"^$\")",
"Bash(du -sh /var/log/*)",
"Bash(lsmod)",
"Bash(xargs ls:*)",
"Bash(last:*)",
"Bash(nginx:*)",
"Bash(redis-cli:*)",
"Bash(rkhunter:*)",
"Bash(aideinit)",
"Bash(npm --version)",
"Bash(ruby --version)",
"Bash(getent passwd:*)",
"Bash(sqlite3:*)",
"Read(//home/**)",
"Read(//opt/**)",
"Read(//etc/nginx/sites-enabled/**)",
"Read(//etc/nginx/sites-available/**)"
],
"defaultMode": "auto"
},
"theme": "dark-daltonized",
"verbose": true,
"tui": "fullscreen",
"skipAutoPermissionPrompt": true
}
+56 -20
View File
@@ -1,33 +1,69 @@
#!/bin/bash #!/bin/bash
# #
# Print the "best" IP for the tmux status bar. # Print the "best" IP for the tmux status bar.
# Preference: DO reserved/floating IP > public egress (ipify) > first local IP. #
# Shows a directly-held public/WAN IP when the machine has one on an interface,
# otherwise its LAN/private IP. No external "what's my IP" lookup -- a NAT'd box
# shows its LAN IP, never the gateway's egress. Works the same everywhere: a box
# with a public IP on a NIC (incl. a cloud droplet's eth0) shows that WAN IP; a
# NAT'd box shows its LAN IP. The VPN overlay is added separately by tmux-net via
# tmux-vpn-ip and is untouched here.
# #
set -u set -u
META="http://169.254.169.254/metadata/v1" # This machine's own non-loopback IPv4 addresses, one per line. VPN tunnels are
CURL="curl -sf --connect-timeout 1 --max-time 2" # excluded (tmux-net surfaces the VPN address separately as the ⇡ segment).
list_ipv4() {
is_digitalocean() { if command -v ip >/dev/null 2>&1; then
[ -r /sys/class/dmi/id/sys_vendor ] \ ip -4 -o addr show scope global 2>/dev/null \
&& grep -qi digitalocean /sys/class/dmi/id/sys_vendor | awk '$2 !~ /^(tun|wg|ppp|tap)/ {print $4}' | cut -d/ -f1
elif hostname -I >/dev/null 2>&1; then
hostname -I 2>/dev/null | tr ' ' '\n'
else
ifconfig 2>/dev/null | awk '/inet /{print $2}'
fi | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$' | grep -v '^127\.'
} }
do_anchor_ip() { # RFC1918 / link-local / CGNAT -- not a directly-routable WAN address.
local kind active ip is_private_ipv4() {
for kind in reserved_ip floating_ip; do case "$1" in
active=$($CURL "$META/$kind/ipv4/active" 2>/dev/null) || continue 10.*|192.168.*|169.254.*) return 0 ;;
[ "$active" = "true" ] || continue 172.1[6-9].*|172.2[0-9].*|172.3[01].*) return 0 ;;
ip=$($CURL "$META/$kind/ipv4/ip_address" 2>/dev/null) || continue 100.6[4-9].*|100.[7-9][0-9].*|100.1[01][0-9].*|100.12[0-7].*) return 0 ;;
[ -n "$ip" ] && { printf '%s\n' "$ip"; return 0; } *) return 1 ;;
done esac
}
# First directly-held public/WAN IP, if the machine has one on an interface.
public_ip() {
local ip
while IFS= read -r ip; do
[ -n "$ip" ] || continue
is_private_ipv4 "$ip" || { printf '%s\n' "$ip"; return 0; }
done <<EOF
$(list_ipv4)
EOF
return 1 return 1
} }
if is_digitalocean && do_anchor_ip; then # Fallback: the machine's primary interface IP (covers macOS and odd setups).
exit 0 print_local_ip() {
fi local iface ip
if command -v ipconfig >/dev/null 2>&1; then
for iface in en0 en1 en2; do
ip=$(ipconfig getifaddr "$iface" 2>/dev/null) || true
[ -n "${ip:-}" ] && { printf '%s\n' "$ip"; return 0; }
done
fi
if hostname -I >/dev/null 2>&1; then
hostname -I | awk '{print $1}'
return 0
fi
ifconfig 2>/dev/null | awk '/inet /{ if ($2 != "127.0.0.1") { print $2; exit } }'
}
curl -s --connect-timeout 3 https://api.ipify.org 2>/dev/null \ # A directly-held public/WAN IP if the box has one, else the LAN IP.
|| hostname -I | awk '{print $1}' ip=$(public_ip || true)
[ -z "${ip:-}" ] && ip=$(print_local_ip)
printf '%s\n' "${ip:-}"
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
#
# Emit the tmux status-bar network segment.
# No VPN: "⌂ <local_ip>"
# VPN up: "⌂ <local_ip> / ⇡ <vpn_ip>"
# Offline: "✕" (no local IP and no VPN tunnel)
#
set -u
local_ip=$("$HOME/.local/bin/tmux-ip" 2>/dev/null || true)
vpn_ip=$("$HOME/.local/bin/tmux-vpn-ip" 2>/dev/null || true)
if [ -z "${local_ip:-}" ] && [ -z "${vpn_ip:-}" ]; then
printf '\xe2\x9c\x95 \n' # ✕ no active connection (trailing space for padding)
elif [ -n "${vpn_ip:-}" ]; then
printf '\xe2\x8c\x82 %s / \xe2\x87\xa1 %s\n' "${local_ip:-}" "$vpn_ip"
else
printf '\xe2\x8c\x82 %s\n' "${local_ip:-}"
fi
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
#
# Print the IPv4 of the first active VPN tunnel interface, if any.
# Empty output when no VPN is up.
#
# macOS: utun* Linux: tun*, wg*, ppp*
set -u
case "$(uname -s)" in
Darwin)
ifconfig 2>/dev/null | awk '
/^utun[0-9]+:/ { iface=$1; sub(":", "", iface); next }
/^[a-z]+[0-9]*:/ { iface="" }
iface != "" && $1 == "inet" { print $2; exit }
'
;;
Linux)
if command -v ip >/dev/null 2>&1; then
ip -4 -o addr show 2>/dev/null \
| awk '$2 ~ /^(tun|wg|ppp)[0-9]+/ { sub("/.*","",$4); print $4; exit }'
fi
;;
esac
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+17 -4
View File
@@ -2,7 +2,20 @@
set -g bell-action any set -g bell-action any
# Default termtype. If the rcfile sets $TERM, that overrides this value. # Default termtype. If the rcfile sets $TERM, that overrides this value.
set -g default-terminal screen-256color set -g default-terminal "tmux-256color"
# Truecolor + clipboard passthrough for outer terminals that advertise RGB
# (covers xterm-*, alacritty, kitty, wezterm, iTerm, ghostty, modern Apple Terminal).
set -ga terminal-features ",xterm-256color:RGB"
set -ga terminal-features ",alacritty:RGB"
set -ga terminal-features ",kitty:RGB"
set -ga terminal-features ",wezterm:RGB"
set -ga terminal-features ",ghostty:RGB"
set -ga terminal-features ",screen-256color:RGB"
# TUI apps (Claude Code, nvim, etc.) want focus events so they can redraw
# / refresh state when the pane regains focus.
set -g focus-events on
# Keep your finger on ctrl, or don't # Keep your finger on ctrl, or don't
bind-key ^D detach-client bind-key ^D detach-client
@@ -66,9 +79,9 @@ set -g pane-border-style fg=colour245
set -g pane-active-border-style fg=colour39 set -g pane-active-border-style fg=colour39
set -g message-style fg=colour16,bg=colour221,bold set -g message-style fg=colour16,bg=colour221,bold
set -g status-left '#[fg=colour235,bg=colour252,bold] ❐ #S #[fg=colour252,bg=colour238,nobold]#[fg=colour245,bg=colour238,bold] #(whoami) ' set -g status-left '#[fg=colour235,bg=colour252,bold] ❐ #S #[fg=colour252,bg=colour238,nobold]#[fg=colour245,bg=colour238,bold] #(whoami) '
set -g status-right '#[bold][#[nobold,fg=colour229]#H#[fg=default] / #[fg=colour229]#(~/.local/bin/tmux-ip)#[fg=default,bold]]#[nobold,fg=colour255] %H:%M %d-%b-%Y ' set -g status-right '#[bold][#[nobold,fg=colour229]#h#[fg=default] / #[fg=colour229]#(~/.local/bin/tmux-net)#[fg=default,bold]]#[nobold,fg=colour255] %-I:%M%P %d-%b-%Y '
set -g window-status-format '#[fg=colour235,bg=colour252,nobold] #(~/.local/bin/tmux-window-icon #{window_index}) #(pwd="#{pane_current_path}"; echo ${pwd####*/}) #W ' set -g window-status-format '#[fg=colour235,bg=colour252,nobold] #{?#{==:#{window_index},1},❶,#{?#{==:#{window_index},2},❷,#{?#{==:#{window_index},3},❸,#{?#{==:#{window_index},4},❹,#{?#{==:#{window_index},5},❺,#{?#{==:#{window_index},6},❻,#{?#{==:#{window_index},7},❼,#{?#{==:#{window_index},8},❽,#{?#{==:#{window_index},9},❾,#{?#{==:#{window_index},10},❿,#{window_index}}}}}}}}}}} #{b:pane_current_path} #W '
set -g window-status-current-format '#[fg=colour234,bg=colour39,bold] [#[fg=colour232,bold]#{?window_zoomed_flag,#[fg=colour228],} #(~/.local/bin/tmux-window-icon #{window_index}) #(pwd="#{pane_current_path}"; echo ${pwd####*/}) #W #[fg=colour234,bold]] ' set -g window-status-current-format '#[fg=colour234,bg=colour39,bold] [#[fg=colour232,bold]#{?window_zoomed_flag,#[fg=colour228],} #{?#{==:#{window_index},1},❶,#{?#{==:#{window_index},2},❷,#{?#{==:#{window_index},3},❸,#{?#{==:#{window_index},4},❹,#{?#{==:#{window_index},5},❺,#{?#{==:#{window_index},6},❻,#{?#{==:#{window_index},7},❼,#{?#{==:#{window_index},8},❽,#{?#{==:#{window_index},9},❾,#{?#{==:#{window_index},10},❿,#{window_index}}}}}}}}}}} #{b:pane_current_path} #W #[fg=colour234,bold]] '
set-option -g status-interval 60 set-option -g status-interval 60
# Patch for OS X pbpaste and pbcopy under tmux. # Patch for OS X pbpaste and pbcopy under tmux.
+40
View File
@@ -102,6 +102,46 @@ On top of stock YADR:
- Misc: `.rainbow_config.json` (rainbowstream), `.nanorc`, `.selected_editor`, - Misc: `.rainbow_config.json` (rainbowstream), `.nanorc`, `.selected_editor`,
`.mplayer/config`, `.jigdo-lite` `.mplayer/config`, `.jigdo-lite`
## Rainbow output (lolcat)
`zsh/lolcat.zsh` wires [lolcat](https://github.com/busyloop/lolcat) (Debian's
`/usr/games/lolcat`, busyloop's Ruby build — 24-bit truecolor) into a few shell
helpers. lolcat auto-detects a tty — it colorizes straight to the terminal but
passes text through plain when piped onward, so use `-f`/`--force` to colorize
mid-pipeline.
### Opt-in, per command
Prefix any command to rainbow its output:
```sh
lol fastfetch
lol git log --oneline
lol ls -C # -C keeps columns: a piped ls otherwise goes single-column
lolf git diff | less -R # force color so it survives down a pipeline
```
### Shortcuts
```sh
rcat file.txt # rainbow cat
rls -la # rainbow ls, columns preserved
some-cmd | rless # rainbow + pager (less -R)
```
### Session toggle
Rainbow *all* shell stdout until you turn it off:
```sh
rainbow_on # everything is rainbow now
...
rainbow_off # back to normal
```
Novelty only: it garbles full-screen/interactive programs (vim, less, htop,
fzf, ssh) and the prompt, and buffers streaming output. Doesn't touch stderr.
## Upstream ## Upstream
Base is [skwp/dotfiles][yadr] (YADR — Yet Another Dotfiles Repo). All credit Base is [skwp/dotfiles][yadr] (YADR — Yet Another Dotfiles Repo). All credit
+9 -2
View File
@@ -210,7 +210,7 @@ def install_term_theme
message = "Which theme would you like to apply to your iTerm2 profile?" message = "Which theme would you like to apply to your iTerm2 profile?"
color_scheme = ask message, iTerm_available_themes color_scheme = ask message, iTerm_available_themes
return if color_scheme == 'None' return if color_scheme.nil? || color_scheme == 'None'
color_scheme_file = File.join('iTerm2', "#{color_scheme}.itermcolors") color_scheme_file = File.join('iTerm2', "#{color_scheme}.itermcolors")
@@ -220,6 +220,8 @@ def install_term_theme
profiles << 'All' profiles << 'All'
selected = ask message, profiles selected = ask message, profiles
return if selected.nil?
if selected == 'All' if selected == 'All'
(profiles.size-1).times { |idx| apply_theme_to_iterm_profile_idx idx, color_scheme_file } (profiles.size-1).times { |idx| apply_theme_to_iterm_profile_idx idx, color_scheme_file }
else else
@@ -244,7 +246,12 @@ def ask(message, values)
puts message puts message
while true while true
values.each_with_index { |val, idx| puts " #{idx+1}. #{val}" } values.each_with_index { |val, idx| puts " #{idx+1}. #{val}" }
selection = STDIN.gets.chomp input = STDIN.gets
if input.nil?
puts "(no input available — skipping prompt)"
return nil
end
selection = input.chomp
if (Float(selection)==nil rescue true) || selection.to_i < 0 || selection.to_i > values.size+1 if (Float(selection)==nil rescue true) || selection.to_i < 0 || selection.to_i > values.size+1
puts "ERROR: Invalid selection.\n\n" puts "ERROR: Invalid selection.\n\n"
else else
+4 -1
View File
@@ -1,5 +1,8 @@
# set your user tokens as environment variables, such as ~/.secrets # set your user tokens as environment variables, such as ~/.secrets
# See the README for examples. # See the README for examples.
[user]
name = dissimulo
email = connect+gitea@dustin-williams.com
[color] [color]
ui = true ui = true
[color "branch"] [color "branch"]
@@ -126,4 +129,4 @@
# http://gitfu.wordpress.com/2008/04/20/git-rerere-rereremember-what-you-did-last-time/ # http://gitfu.wordpress.com/2008/04/20/git-rerere-rereremember-what-you-did-last-time/
enabled = true enabled = true
[include] [include]
path = .gitconfig.user path = ~/.gitconfig.user
+98 -20
View File
@@ -30,9 +30,13 @@ PERSONAL_FILES=(
.zprofile .zshenv .zshrc .zprofile .zshenv .zshrc
) )
# Directories deployed wholesale to $HOME/<dir>/. # Directories whose contents are merged into $HOME/<dir>/, file by file. Files
# the repo provides replace conflicting on-disk versions (with the displaced
# version preserved in $BACKUP_DIR); files outside the repo's tree are left
# untouched. This deliberately avoids wholesale-replacing $HOME/.local etc.,
# which would sweep away unrelated user data (fonts, app state, ...).
PERSONAL_DIRS=( PERSONAL_DIRS=(
.fonts .irssi .nano .themes .local .mplayer .fonts .irssi .nano .themes .local .mplayer .claude
) )
have() { command -v "$1" >/dev/null 2>&1; } have() { command -v "$1" >/dev/null 2>&1; }
@@ -83,37 +87,104 @@ else
fi fi
# 3. Deploy personal dotfiles # 3. Deploy personal dotfiles
backup_one() {
local src_name="$1" # Move $HOME/<rel> aside into $BACKUP_DIR, preserving its sub-path.
local dst="$HOME/$src_name" backup_one_path() {
local rel="$1"
local dst="$HOME/$rel"
if [ -e "$dst" ] || [ -L "$dst" ]; then if [ -e "$dst" ] || [ -L "$dst" ]; then
mkdir -p "$BACKUP_DIR/$(dirname "$src_name")" mkdir -p "$BACKUP_DIR/$(dirname "$rel")"
mv "$dst" "$BACKUP_DIR/$src_name" mv "$dst" "$BACKUP_DIR/$rel"
fi fi
} }
deploy_one() { # Returns 0 iff $1 and $2 are equivalent (same symlink target, or identical
local name="$1" # regular-file content). Used to make re-runs idempotent: unchanged files get
local src="$YADR_DIR/$name" # skipped instead of needlessly backed up.
local dst="$HOME/$name" paths_equivalent() {
local a="$1" b="$2"
if [ -L "$a" ] && [ -L "$b" ]; then
[ "$(readlink "$a")" = "$(readlink "$b")" ]
elif [ -L "$a" ] || [ -L "$b" ]; then
return 1
elif [ -f "$a" ] && [ -f "$b" ]; then
cmp -s "$a" "$b"
else
return 1
fi
}
# Deploy one file or symlink at $YADR_DIR/<rel> to $HOME/<rel>. If a different
# file/symlink is already at the destination, it's moved into $BACKUP_DIR first.
deploy_file() {
local rel="$1"
local src="$YADR_DIR/$rel"
local dst="$HOME/$rel"
if [ ! -e "$src" ] && [ ! -L "$src" ]; then if [ ! -e "$src" ] && [ ! -L "$src" ]; then
return return
fi fi
backup_one "$name" if paths_equivalent "$src" "$dst"; then
return
fi
backup_one_path "$rel"
mkdir -p "$(dirname "$dst")" mkdir -p "$(dirname "$dst")"
cp -a "$src" "$dst" cp -a "$src" "$dst"
info "$name" info "$rel"
}
# Deploy a directory by walking its tree and deploying each file/symlink in
# turn. Directories get mkdir'd; existing entries inside $HOME/<rel> that the
# repo doesn't know about are left alone. This is the merge semantics that
# protects e.g. $HOME/.local/share/fonts from being clobbered when the repo
# only tracks $HOME/.local/bin.
deploy_dir() {
local rel="$1"
local src="$YADR_DIR/$rel"
if [ ! -d "$src" ] || [ -L "$src" ]; then
return
fi
mkdir -p "$HOME/$rel"
local sub sub_rel src_path
while IFS= read -r -d '' sub; do
sub="${sub#./}"
[ "$sub" = "." ] && continue
sub_rel="$rel/$sub"
src_path="$YADR_DIR/$sub_rel"
if [ -d "$src_path" ] && [ ! -L "$src_path" ]; then
mkdir -p "$HOME/$sub_rel"
continue
fi
deploy_file "$sub_rel"
done < <(cd "$src" && find . -mindepth 1 -print0)
} }
log "Deploying personal dotfiles" log "Deploying personal dotfiles"
for f in "${PERSONAL_FILES[@]}"; do deploy_one "$f"; done for f in "${PERSONAL_FILES[@]}"; do deploy_file "$f"; done
for d in "${PERSONAL_DIRS[@]}"; do deploy_one "$d"; done for d in "${PERSONAL_DIRS[@]}"; do deploy_dir "$d"; done
# .local/bin/claude is a relative symlink to ../share/claude/versions/... # .local/bin/claude is a relative symlink to ../share/claude/versions/<version>.
# If that target isn't present on this machine, remove the dangling link so # The version baked into the repo snapshot is whatever was current when this
# Claude Code's own installer (next step) can manage it. # was last committed; on a fresh host that exact version probably isn't
if [ -L "$HOME/.local/bin/claude" ] && [ ! -e "$HOME/.local/bin/claude" ]; then # installed. Resolve as follows:
rm "$HOME/.local/bin/claude" # 1. If the deployed symlink already resolves, leave it alone.
# 2. Else, if any other Claude version is present under
# ~/.local/share/claude/versions/, repoint the symlink to the highest
# one (semver sort) so users get the latest installed binary.
# 3. Else, drop the dangling link and let Claude Code's own installer
# (next step) recreate it.
claude_link="$HOME/.local/bin/claude"
claude_versions_dir="$HOME/.local/share/claude/versions"
if [ -L "$claude_link" ] && [ ! -e "$claude_link" ]; then
latest_claude=""
if [ -d "$claude_versions_dir" ]; then
latest_claude="$(ls -1 "$claude_versions_dir" 2>/dev/null | sort -V | tail -1)"
fi
if [ -n "$latest_claude" ] && [ -e "$claude_versions_dir/$latest_claude" ]; then
ln -sfn "../share/claude/versions/$latest_claude" "$claude_link"
info "Repointed ~/.local/bin/claude -> $latest_claude (latest installed)"
else
rm "$claude_link"
fi
fi fi
# 4. Install Claude Code (Anthropic's CLI). Skip if already installed or # 4. Install Claude Code (Anthropic's CLI). Skip if already installed or
@@ -137,6 +208,13 @@ if [ -d "$HOME/.local/bin" ]; then
find "$HOME/.local/bin" -maxdepth 1 -type f -exec chmod +x {} \; 2>/dev/null || true find "$HOME/.local/bin" -maxdepth 1 -type f -exec chmod +x {} \; 2>/dev/null || true
fi fi
# 5. Refresh font cache so newly-deployed .fonts/ and .local/share/fonts/
# entries are visible to apps without a logout/login.
if have fc-cache; then
log "Refreshing font cache"
fc-cache -f >/dev/null 2>&1 || warn "fc-cache returned non-zero; continuing"
fi
echo echo
log "Done." log "Done."
if [ -d "$BACKUP_DIR" ]; then if [ -d "$BACKUP_DIR" ]; then
-102
View File
@@ -1,102 +0,0 @@
# Ring the bell if any background window rang a bell
set -g bell-action any
# Default termtype. If the rcfile sets $TERM, that overrides this value.
set -g default-terminal screen-256color
# Keep your finger on ctrl, or don't
bind-key ^D detach-client
# Create splits and vertical splits
bind-key v split-window -h -p 50 -c "#{pane_current_path}"
bind-key ^V split-window -h -p 50 -c "#{pane_current_path}"
bind-key s split-window -p 50 -c "#{pane_current_path}"
bind-key ^S split-window -p 50 -c "#{pane_current_path}"
# Pane resize in all four directions using vi bindings.
# Can use these raw but I map them to shift-ctrl-<h,j,k,l> in iTerm.
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5
# Smart pane switching with awareness of Vim splits.
# See: https://github.com/christoomey/vim-tmux-navigator
is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
| grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'"
bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L'
bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D'
bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U'
bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R'
tmux_version='$(tmux -V | sed -En "s/^tmux ([0-9]+(.[0-9]+)?).*/\1/p")'
if-shell -b '[ "$(echo "$tmux_version < 3.0" | bc)" = 1 ]' \
"bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\' 'select-pane -l'"
if-shell -b '[ "$(echo "$tmux_version >= 3.0" | bc)" = 1 ]' \
"bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\\\' 'select-pane -l'"
bind-key -T copy-mode-vi 'C-h' select-pane -L
bind-key -T copy-mode-vi 'C-j' select-pane -D
bind-key -T copy-mode-vi 'C-k' select-pane -U
bind-key -T copy-mode-vi 'C-l' select-pane -R
bind-key -T copy-mode-vi 'C-\' select-pane -l
# Use vi keybindings for tmux commandline input.
# Note that to get command mode you need to hit ESC twice...
set -g status-keys vi
# Use vi keybindings in copy and choice modes
setw -g mode-keys vi
# easily toggle synchronization (mnemonic: e is for echo)
# sends input to all panes in a given window.
bind e setw synchronize-panes on
bind E setw synchronize-panes off
# set first window to index 1 (not 0) to map more to the keyboard layout...
set-option -g base-index 1
set-window-option -g pane-base-index 1
set-window-option -g mouse on
# color scheme (styled as vim-powerline)
set -g status-left-length 52
set -g status-right-length 451
set -g status-style fg=white,bg=colour234
set -g pane-border-style fg=colour245
set -g pane-active-border-style fg=colour39
set -g message-style fg=colour16,bg=colour221,bold
set -g status-left '#[fg=colour235,bg=colour252,bold] ❐ #S #[fg=colour252,bg=colour238,nobold]⮀#[fg=colour245,bg=colour238,bold] #(whoami) #[fg=colour238,bg=colour234,nobold]⮀'
set -g window-status-format '#[fg=colour235,bg=colour252,bold] #I #(pwd="#{pane_current_path}"; echo ${pwd####*/}) #W '
set -g window-status-current-format '#[fg=colour234,bg=colour39]⮀#[fg=black,bg=colour39,noreverse,bold] #{?window_zoomed_flag,#[fg=colour228],} #I #(pwd="#{pane_current_path}"; echo ${pwd####*/}) #W #[fg=colour39,bg=colour234,nobold]⮀'
set-option -g status-interval 2
# Patch for OS X pbpaste and pbcopy under tmux.
set-option -g default-command "which reattach-to-user-namespace > /dev/null && reattach-to-user-namespace -l $SHELL || $SHELL"
# Screen like binding
unbind C-b
set -g prefix C-a
bind a send-prefix
# No escape time for vi mode
set -sg escape-time 0
# Screen like binding for last window
unbind l
bind C-a last-window
# Bigger history
set -g history-limit 10000
# New windows/pane in $PWD
bind c new-window -c "#{pane_current_path}"
# Fix key bindings broken in tmux 2.1
set -g assume-paste-time 0
# force a reload of the config file
unbind r
bind r source-file ~/.tmux.conf \; display "Reloaded!"
# Local config
if-shell "[ -f ~/.tmux.conf.user ]" 'source ~/.tmux.conf.user'
+1
View File
@@ -0,0 +1 @@
../.tmux.conf
+13
View File
@@ -0,0 +1,13 @@
# Eagerly define `diff` as a real function instead of relying on prezto's
# autoload stub. The autoload stub emits
# "(eval):1: diff: function definition file not found"
# in non-interactive eval contexts where $fpath doesn't yet include the
# prezto utility module's functions directory. Defining a real function
# here bypasses the autoload path entirely.
function diff {
if (( $+commands[colordiff] )); then
command diff --unified "$@" | colordiff --difftype diffu
else
command diff --unified "$@"
fi
}
+33
View File
@@ -0,0 +1,33 @@
# zsh/lolcat.zsh (drunkendotfiles) — auto-sourced via ~/.zshrc: for f in ~/.yadr/zsh/*.zsh
# Rainbow-output helpers built on lolcat = Debian's busyloop Ruby build at
# /usr/games/lolcat (upstream github.com/busyloop/lolcat; 24-bit truecolor).
# It auto-detects a tty, so use -f/--force-color to colorize mid-pipeline.
# This lolcat is truecolor and auto-detects a tty: it colorizes when writing
# straight to the terminal, but passes text through UNcolored when piped onward,
# so use -f/--force to colorize in the middle of a pipeline.
# Debian ships lolcat in /usr/games; make sure it's reachable in any shell.
[[ ":$PATH:" == *":/usr/games:"* ]] || export PATH="$PATH:/usr/games"
# --- opt-in: prefix ANY command to rainbow its output on the terminal --------
# lol ls -C lol fastfetch lol git log --oneline
# (The wrapped command sees a pipe, not a tty, so some tools drop their own
# colors/columns -- e.g. use `lol ls -C` to keep ls in columns.)
lol() { "$@" | lolcat; }
# Same, but force color so it survives further down a pipeline:
# lolf git diff | less -R
lolf() { "$@" | lolcat -f; }
# --- handy rainbow shortcuts for plain-text output ---------------------------
rcat() { lolcat "$@"; } # rcat file.txt (or: some-cmd | rcat)
rls() { ls -C "$@" | lolcat; } # rainbow ls, columns preserved
rless() { lolcat -f | less -R; } # pipe into me: some-cmd | rless
# --- whole-session "rainbow everything" toggle (NOVELTY) ---------------------
# Pipes the shell's stdout through lolcat until turned off. It WILL garble
# full-screen/interactive programs (vim, less, htop, fzf, ssh) and your prompt,
# and buffers streaming output. Use it for a burst of plain command output,
# then rainbow_off. Does not affect stderr.
rainbow_on() { [[ -n $RAINBOW_PIPE ]] && return; exec 3>&1; exec > >(lolcat -f); export RAINBOW_PIPE=1; }
rainbow_off() { [[ -z $RAINBOW_PIPE ]] && return; exec 1>&3 3>&-; unset RAINBOW_PIPE; }
+1
View File
@@ -1,3 +1,4 @@
source $HOME/.zprezto/runcoms/zshrc source $HOME/.zprezto/runcoms/zshrc
for config_file ($HOME/.yadr/zsh/*.zsh) source $config_file for config_file ($HOME/.yadr/zsh/*.zsh) source $config_file
export PATH="$HOME/.local/bin:$PATH"