Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ brew 'bash'
brew 'macvim'
brew 'neovim'
brew 'the_silver_searcher'
brew 'tmux'
brew 'zsh'
brew 'zsh-autosuggestions'
brew 'zsh-syntax-highlighting'
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# dotfiles

Personal macOS development environment managed with [Homesick][homesick_link]. Configures Zsh (with
[Oh My Zsh][oh_my_zsh_link]), Vim via [Vundle][vundle_link], Ruby development tools via
[Oh My Zsh][oh_my_zsh_link]), Vim via [Vundle][vundle_link], tmux, Ruby development tools via
[rbenv][rbenv_link], and a curated set of [Homebrew][homebrew_link] packages for web development.
Uses [mise][mise_link] for managing non-Ruby tool versions.

Expand Down
97 changes: 97 additions & 0 deletions home/.tmux.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Advertise tmux's own terminfo rather than inheriting the outer TERM. Programs then look up
# capabilities tmux actually implements; `xterm-256color` promises xterm features tmux lacks, which
# is what produces stray escape sequences in vim and full-screen apps. Truecolor is a separate
# capability the outer terminal has to be trusted for, so it is declared for that TERM specifically.
set -g default-terminal "tmux-256color"
set -as terminal-features ",xterm-256color:RGB"

# How long tmux waits after an Escape to see whether more bytes follow (a real escape sequence)
# before forwarding it as a bare Esc. Recent tmux already defaults to 10ms, but it spent years at
# 500ms — long enough to put a visible delay in front of every Esc, including the vi-mode keys
# below and Claude Code's interrupt. Pinned so an older tmux behaves the same as this one.
set -s escape-time 10

# Let vim and Claude Code see focus gain/loss so they can redraw or autosave on tab switches
set -g focus-events on

set -g history-limit 50000
set -g display-time 2000
set -g mouse on

# Copying tmux's buffer out to the terminal's clipboard over OSC 52 already happens at the default
# `external` — that is the half that carries a yank back through SSH to the local clipboard,
# including an iPhone's. `on` adds the other direction: an application running in a pane may use
# OSC 52 to populate a tmux buffer, so a copy made inside vim reaches tmux's own paste buffer.
set -g set-clipboard on

# Permit escape sequences tmux does not understand to reach the outer terminal, wrapped in tmux's
# DCS passthrough. The iTerm2 tab-title system in ~/.zsh/iterm2-tab-title.zsh depends on this to
# keep publishing its user variables from inside a tmux pane.
set -g allow-passthrough on

# Windows and panes count from 1: the 0 key sits past 9 on a keyboard, so a 0-indexed first window
# is the one furthest from the prefix. Renumbering keeps the sequence gapless after a close.
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on

# Size each window to the smallest client *currently looking at it*, rather than to the smallest
# client attached to the session at all. This is what keeps a desktop window full-size while a
# phone is also attached to the same session but viewing something else.
setw -g aggressive-resize on

# Match the shell's vi-mode plugin. tmux already infers this from $EDITOR, which is `mvim -v` here
# and so reads as vi — pinning it keeps copy mode from changing keys if $EDITOR ever does.
setw -g mode-keys vi
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi C-v send -X rectangle-toggle
bind -T copy-mode-vi y send -X copy-pipe-and-cancel "pbcopy"
bind -T copy-mode-vi Escape send -X cancel

# Splits that keep the current directory, on keys shaped like the split they make. The defaults
# (% and ") are unbound rather than left as duplicates so there is one way to do it.
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
unbind %
unbind '"'

# Pane movement on vim's home row. This takes over the default prefix+l (last-window), which moves
# to prefix+Tab below.
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
bind Tab last-window

# -r makes these repeat without re-pressing the prefix, so a pane can be dragged to size in one go
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

bind r source-file ~/.tmux.conf \; display-message "~/.tmux.conf reloaded"

# Inside tmux the shell's own title sequences terminate at tmux, so without this the outer terminal
# title freezes at whatever was set before tmux started. `#T` is the active pane's title, which
# ~/.zsh/iterm2-tab-title.zsh sets to the git branch (or the running command) — republishing that
# unchanged keeps iTerm2 tabs reading exactly as they do outside tmux, branch first.
set -g set-titles on
set -g set-titles-string "#T"

# Status line: `bg=default` inherits the terminal background instead of painting a colored bar, so
# this reads correctly against any iTerm2 profile. The current window is marked by weight and
# color rather than a highlight block, for the same reason.
set -g status-position bottom
set -g status-style "bg=default,fg=colour245"
set -g status-left "#[fg=colour109,bold] #S #[default]"
set -g status-left-length 30
set -g status-right "#[fg=colour245] %-d %b %-I:%M %p "
set -g status-right-length 30
setw -g window-status-format " #I #W "
setw -g window-status-current-format " #I #W "
setw -g window-status-current-style "fg=colour223,bold"
setw -g window-status-bell-style "fg=colour167,bold"
set -g pane-border-style "fg=colour238"
set -g pane-active-border-style "fg=colour109"
set -g message-style "fg=colour223,bg=colour238"
4 changes: 4 additions & 0 deletions home/.zsh/aliases.zsh
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Shell
alias e='exec'
alias ta='tmux -2 attach || tn'
alias tn='tmux -2 new'
# Attach if it exists, create it otherwise: one command for starting and for resuming after a
# dropped connection, which is the whole point of the session surviving on the far end
alias tm='tmux -2 new -A -s mobile'
alias vim='mvim -v'

# Ruby
Expand Down
24 changes: 21 additions & 3 deletions home/.zsh/iterm2-tab-title.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,25 @@ DISABLE_AUTO_TITLE="true"
_set_iterm2_user_var() {
local b64
b64=$(printf '%s' "$2" | base64 | tr -d '\n')
printf '\e]1337;SetUserVar=%s=%s\a' "$1" "$b64"
# tmux swallows escape sequences it does not implement, so inside a pane this one has to be
# wrapped in tmux's DCS passthrough (with its own ESC doubled) to reach iTerm2. Depends on
# `allow-passthrough on` in ~/.tmux.conf.
if [[ -n $TMUX ]]; then
printf '\ePtmux;\e\e]1337;SetUserVar=%s=%s\a\e\\' "$1" "$b64"
else
printf '\e]1337;SetUserVar=%s=%s\a' "$1" "$b64"
fi
}

# tmux ignores OSC 1 (icon name) but acts on OSC 2, so inside a pane the title has to go out as
# OSC 2 to land in tmux's pane title; `set-titles-string "#T"` then republishes it to iTerm2.
# Outside tmux, OSC 1 remains the one that names the tab without also retitling the window.
_tab_title_emit() {
if [[ -n $TMUX ]]; then
printf '\e]2;%s\a' "$1"
else
printf '\e]1;%s\a' "$1"
fi
}

_tab_title_idle() {
Expand All @@ -23,12 +41,12 @@ _tab_title_idle() {
[[ -z $branch ]] && branch="no-git"
dir=${${PWD/#$HOME/~}:t}
_set_iterm2_user_var dir "$dir"
printf '\e]1;%s\a' "$branch"
_tab_title_emit "$branch"
}

# Fallback title for processes that don't emit their own OSC title. Claude Code, vim with
# titlestring, ssh, tmux, etc. will overwrite this almost immediately, which is what we want.
_tab_title_running() { printf '\e]1;▶ %s\a' "$1" }
_tab_title_running() { _tab_title_emit "▶ $1" }

autoload -Uz add-zsh-hook
add-zsh-hook precmd _tab_title_idle
Expand Down