mirror of
https://github.com/boxpositron/absolute-dotfiles.git
synced 2026-10-24 08:23:19 +00:00
feat(tmux): ctrl+click a file path to open it by type
tmux-open-path takes the path under the mouse, strips :line:col, #L, @ and file:// decorations, and resolves it against the pane directory and then its git root. Markdown opens in Markdown Preview (added to Brewfile.apps), images, PDFs and directories in yazi, and everything else in nvim at the clicked line, both in a split beside the pane.
This commit is contained in:
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env zsh
|
||||
# Open the file path under the mouse in a tmux pane, routed by type:
|
||||
# markdown -> Markdown Preview (GUI)
|
||||
# images, pdf, dirs -> yazi in a split pane (image preview via passthrough)
|
||||
# anything else -> nvim in a split pane, jumping to :line when present
|
||||
#
|
||||
# Called from a tmux mouse binding:
|
||||
# tmux-open-path <pane_id> <pane_current_path> <mouse_x> <mouse_line>
|
||||
|
||||
emulate -L zsh
|
||||
setopt extendedglob
|
||||
|
||||
pane=$1 cwd=$2 x=$3 line=$4
|
||||
|
||||
fail() { tmux display-message -d 2000 "open-path: $1"; exit 1 }
|
||||
|
||||
[[ -n $pane && -n $cwd && $x == <-> ]] || fail "usage: tmux-open-path PANE CWD X LINE"
|
||||
|
||||
# Grab the run of path-like characters around the clicked column. Whitespace,
|
||||
# quotes, brackets and box-drawing borders end a token.
|
||||
local stop=$' \t"\'`()[]{}<>|│┃║─━'
|
||||
(( i = x + 1 )) # mouse_x is 0-based, zsh strings are 1-based
|
||||
(( i <= ${#line} )) || fail "nothing under cursor"
|
||||
[[ $stop == *${line[i]}* ]] && fail "nothing under cursor"
|
||||
(( s = i )); while (( s > 1 )) && [[ $stop != *${line[s-1]}* ]]; do (( s-- )); done
|
||||
(( e = i )); while (( e < ${#line} )) && [[ $stop != *${line[e+1]}* ]]; do (( e++ )); done
|
||||
token=${line[s,e]}
|
||||
|
||||
# Normalise: file:// URLs, @mentions, trailing punctuation, :line[:col], #Lnn.
|
||||
token=${token#file://}
|
||||
token=${token#@}
|
||||
token=${token%%[.,;:!?]##}
|
||||
lnum=
|
||||
if [[ $token == (#b)(*)\#L(<->)* ]]; then
|
||||
token=$match[1] lnum=$match[2]
|
||||
elif [[ $token == (#b)(*):(<->):<-> || $token == (#b)(*):(<->) ]]; then
|
||||
token=$match[1] lnum=$match[2]
|
||||
fi
|
||||
[[ -n $token ]] || fail "nothing under cursor"
|
||||
|
||||
# Resolve against the pane's directory, then the enclosing git root.
|
||||
token=${~token} # expand a leading ~
|
||||
# (not "path": zsh ties that name to $PATH)
|
||||
file=
|
||||
if [[ $token == /* ]]; then
|
||||
[[ -e $token ]] && file=$token
|
||||
else
|
||||
for base in $cwd $(git -C $cwd rev-parse --show-toplevel 2>/dev/null); do
|
||||
[[ -e $base/$token ]] && { file=$base/$token; break }
|
||||
done
|
||||
fi
|
||||
[[ -n $file ]] || fail "no such file: $token"
|
||||
file=${file:A}
|
||||
|
||||
if [[ -d $file ]]; then
|
||||
tmux split-window -h -t $pane -c $file "yazi ${(q)file}"
|
||||
exit
|
||||
fi
|
||||
|
||||
case ${file:e:l} in
|
||||
md|markdown|mdx|mdown|mkd|mkdn)
|
||||
open -a "Markdown Preview" $file ;;
|
||||
png|jpg|jpeg|gif|webp|avif|svg|bmp|tif|tiff|heic|ico|pdf)
|
||||
tmux split-window -h -t $pane -c ${file:h} "yazi ${(q)file}" ;;
|
||||
*)
|
||||
tmux split-window -h -t $pane -c $cwd "nvim ${lnum:++$lnum} ${(q)file}" ;;
|
||||
esac
|
||||
@@ -142,6 +142,11 @@ bind-key -T copy-mode-vi 'C-\' select-pane -l
|
||||
set -g set-clipboard on
|
||||
set -as terminal-features ',*:clipboard'
|
||||
|
||||
# Ctrl+click a file path in any pane (omo, shell, logs) to open it:
|
||||
# markdown in Markdown Preview, images/pdf/dirs in yazi, the rest in nvim.
|
||||
# yazi and nvim split beside the clicked pane. See ~/.local/bin/tmux-open-path.
|
||||
bind -n C-MouseDown1Pane run-shell -b "~/.local/bin/tmux-open-path #{q:pane_id} #{q:pane_current_path} #{mouse_x} #{q:mouse_line}"
|
||||
|
||||
#
|
||||
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
|
||||
run '~/.tmux/plugins/tpm/tpm'
|
||||
|
||||
@@ -20,6 +20,7 @@ cask "charles"
|
||||
cask "cyberduck"
|
||||
cask "chromium"
|
||||
cask "johannesnagl/tap/showmd", trusted: true
|
||||
cask "markdown-preview"
|
||||
|
||||
# AI and agents
|
||||
cask "conductor"
|
||||
|
||||
@@ -158,7 +158,7 @@ Key features:
|
||||
|
||||
### Development Tools
|
||||
|
||||
- **tmux** (`.tmux.conf`): Terminal multiplexer with custom key bindings; sets the terminal title to the session name, after the omo status marker when there is one
|
||||
- **tmux** (`.tmux.conf`): Terminal multiplexer with custom key bindings; sets the terminal title to the session name, after the omo status marker when there is one. Ctrl+click a file path in any pane (omo, a shell, logs) to open it with `tmux-open-path`: markdown in Markdown Preview, images, PDFs and directories in yazi, anything else in nvim at the clicked `:line`. yazi and nvim open in a split beside the clicked pane
|
||||
- **Zed** (`.config/zed/keymap.json`): Editor key bindings. Settings stay local because they reference machine-specific agent paths
|
||||
- **tmux server** (`.tmux-server.conf`): Lightweight tmux config for remote servers
|
||||
- **Git**: Global gitignore patterns (`.rgignore`, `.gitignore`)
|
||||
@@ -232,6 +232,7 @@ Custom scripts and binaries are stored in `.local/bin/`:
|
||||
- `claude-tmux`: Claude integration with tmux
|
||||
- `cldir`: Change directory with Claude context
|
||||
- `owt`: Git worktree workspaces for coding agents (see [Agent workspaces](#agent-workspaces-owt))
|
||||
- `tmux-open-path`: Backs tmux's Ctrl+click. Takes the path under the mouse, strips `:line:col`, `#L10`, `@` and `file://`, resolves it against the pane's directory and then its git root, and routes it by type: markdown to Markdown Preview (`cask "markdown-preview"` in `Brewfile.apps`), images, PDFs and directories to yazi, the rest to nvim
|
||||
- `omo-notify`: Sound, tab marker and notification for OmO's hooks. Ghostty posts the notification, so clicking it opens the session's tab and pane
|
||||
- `omo-status`: Coloured marker in front of the Ghostty tab title for each tmux session running omo: 🔴 stopped with an error, 🟡 waiting for your answer, 🟢 finished, 🔵 working. A tab with several agents shows the most urgent one; focusing the tab clears red, amber and green, and undoes any title you gave the tab by hand so it always shows its session name
|
||||
- `docker-nuke`: Force-restart a wedged Docker Desktop, capturing diagnostics to `~/.local/state/docker-nuke/` first (`--dry-run` to preview, `--no-restart` to leave it down, `--deep` for root helpers)
|
||||
|
||||
Reference in New Issue
Block a user