pinwheel: Restore claude and emacs panes with tmux-resurrect
This commit is contained in:
5 files changed
+140
-1
No files matched your search
@@ -34,6 +34,16 @@ let
|
|||||||
pr = "";
|
pr = "";
|
||||||
};
|
};
|
||||||
hooks = {
|
hooks = {
|
||||||
|
SessionStart = [
|
||||||
|
{
|
||||||
|
hooks = [
|
||||||
|
{
|
||||||
|
type = "command";
|
||||||
|
command = "tmux-panes claude-session";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
PostToolUse = [
|
PostToolUse = [
|
||||||
{
|
{
|
||||||
matcher = "Write|Edit";
|
matcher = "Write|Edit";
|
||||||
|
|||||||
@@ -332,6 +332,41 @@ https://github.com/justbur/emacs-which-key
|
|||||||
(setq history-length 25)
|
(setq history-length 25)
|
||||||
(savehist-mode 1)
|
(savehist-mode 1)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
** Remember open buffers per tmux pane
|
||||||
|
Used by `tmux-panes` to reopen buffers when tmux-resurrect restores a pane
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(defun tmux-panes--state-file ()
|
||||||
|
(when-let* ((pane (getenv "TMUX_PANE")))
|
||||||
|
(expand-file-name (concat "tmux-panes/" (substring pane 1) "/emacs")
|
||||||
|
(or (getenv "XDG_STATE_HOME") "~/.local/state"))))
|
||||||
|
|
||||||
|
(defun tmux-panes-save-buffers ()
|
||||||
|
(when-let* ((file (tmux-panes--state-file)))
|
||||||
|
(make-directory (file-name-directory file) t)
|
||||||
|
(let ((buffers (delq nil (mapcar (lambda (buffer)
|
||||||
|
(when-let* ((name (buffer-file-name buffer)))
|
||||||
|
(cons name (with-current-buffer buffer (point)))))
|
||||||
|
(buffer-list)))))
|
||||||
|
(with-temp-file file
|
||||||
|
(prin1 buffers (current-buffer))))))
|
||||||
|
|
||||||
|
(defun tmux-panes-restore-buffers ()
|
||||||
|
(when-let* ((file (getenv "TMUX_PANES_RESTORE")))
|
||||||
|
(setenv "TMUX_PANES_RESTORE" nil)
|
||||||
|
(when (file-readable-p file)
|
||||||
|
(let ((buffers (with-temp-buffer
|
||||||
|
(insert-file-contents file)
|
||||||
|
(read (current-buffer)))))
|
||||||
|
(dolist (entry (reverse buffers))
|
||||||
|
(when (file-exists-p (car entry))
|
||||||
|
(find-file (car entry))
|
||||||
|
(goto-char (cdr entry)))))
|
||||||
|
(delete-file file)))
|
||||||
|
(tmux-panes-save-buffers))
|
||||||
|
|
||||||
|
(add-hook 'emacs-startup-hook #'tmux-panes-restore-buffers)
|
||||||
|
(run-with-idle-timer 30 t #'tmux-panes-save-buffers)
|
||||||
|
#+END_SRC
|
||||||
** Vertico
|
** Vertico
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package vertico
|
(use-package vertico
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
e = pkgs.writeShellScriptBin "e" ''
|
e = pkgs.writeShellScriptBin "e" ''
|
||||||
${emacs}/bin/emacs -nw $@
|
exec ${emacs}/bin/emacs -nw "$@"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
ec = pkgs.writeShellScriptBin "ec" ''
|
ec = pkgs.writeShellScriptBin "ec" ''
|
||||||
|
|||||||
@@ -2,9 +2,20 @@
|
|||||||
let
|
let
|
||||||
colors = config.lib.colors.terminal;
|
colors = config.lib.colors.terminal;
|
||||||
dim = builtins.elemAt colors.bright 0;
|
dim = builtins.elemAt colors.bright 0;
|
||||||
|
|
||||||
|
tmuxPanes = pkgs.writeShellApplication {
|
||||||
|
name = "tmux-panes";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.tmux
|
||||||
|
];
|
||||||
|
text = builtins.readFile ./panes.sh;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home-manager.users.alex = {
|
home-manager.users.alex = {
|
||||||
|
home.packages = [ tmuxPanes ];
|
||||||
|
|
||||||
programs.tmux = {
|
programs.tmux = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
@@ -20,6 +31,8 @@ in
|
|||||||
plugin = pkgs.tmuxPlugins.resurrect;
|
plugin = pkgs.tmuxPlugins.resurrect;
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
set -g @resurrect-capture-pane-contents 'on'
|
set -g @resurrect-capture-pane-contents 'on'
|
||||||
|
set -g @resurrect-processes '"~bin/claude->${tmuxPanes}/bin/tmux-panes restore claude" "~bin/emacs -nw->${tmuxPanes}/bin/tmux-panes restore emacs"'
|
||||||
|
set -g @resurrect-hook-post-save-all '${tmuxPanes}/bin/tmux-panes save'
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
state_root="${XDG_STATE_HOME:-$HOME/.local/state}/tmux-panes"
|
||||||
|
snapshot_root="${XDG_DATA_HOME:-$HOME/.local/share}/tmux/resurrect/panes"
|
||||||
|
|
||||||
|
pane_state() {
|
||||||
|
echo "$state_root/${1#%}"
|
||||||
|
}
|
||||||
|
|
||||||
|
pane_snapshot() {
|
||||||
|
echo "$snapshot_root/${1//\//_}"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
claude-session)
|
||||||
|
[ -n "${TMUX_PANE:-}" ] || exit 0
|
||||||
|
session_id="$(jq -r '.session_id')"
|
||||||
|
dir="$(pane_state "$TMUX_PANE")"
|
||||||
|
mkdir -p "$dir"
|
||||||
|
printf '%s\n%s\n' "${CLAUDE_CONFIG_DIR:-}" "$session_id" > "$dir/claude"
|
||||||
|
;;
|
||||||
|
|
||||||
|
save)
|
||||||
|
mkdir -p "$(dirname "$snapshot_root")" "$state_root"
|
||||||
|
snapshot="$(mktemp -d "$snapshot_root.XXXXXX")"
|
||||||
|
live=()
|
||||||
|
|
||||||
|
while IFS=$'\t' read -r id pos cmd; do
|
||||||
|
live+=("${id#%}")
|
||||||
|
case "$cmd" in
|
||||||
|
claude | emacs) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
src="$(pane_state "$id")/$cmd"
|
||||||
|
[ -f "$src" ] || continue
|
||||||
|
dst="$snapshot/${pos//\//_}"
|
||||||
|
mkdir -p "$dst"
|
||||||
|
cp "$src" "$dst/$cmd"
|
||||||
|
done < <(tmux list-panes -a -F '#{pane_id} #S:#I.#P #{pane_current_command}')
|
||||||
|
|
||||||
|
rm -rf "$snapshot_root"
|
||||||
|
mv "$snapshot" "$snapshot_root"
|
||||||
|
|
||||||
|
for dir in "$state_root"/*/; do
|
||||||
|
[ -d "$dir" ] || continue
|
||||||
|
name="$(basename "$dir")"
|
||||||
|
[[ " ${live[*]} " == *" $name "* ]] || rm -rf "$dir"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
|
||||||
|
restore)
|
||||||
|
pos="$(tmux display-message -p -t "$TMUX_PANE" '#S:#I.#P')"
|
||||||
|
file="$(pane_snapshot "$pos")/${2:-}"
|
||||||
|
|
||||||
|
case "${2:-}" in
|
||||||
|
claude)
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "tmux-panes: no saved claude session for $pos" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
{ read -r config_dir; read -r session_id; } < "$file"
|
||||||
|
CLAUDE_CONFIG_DIR="$config_dir" exec claude --resume "$session_id"
|
||||||
|
;;
|
||||||
|
emacs)
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
buffers="$(mktemp)"
|
||||||
|
cp "$file" "$buffers"
|
||||||
|
TMUX_PANES_RESTORE="$buffers" exec e
|
||||||
|
fi
|
||||||
|
exec e
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: tmux-panes restore claude|emacs" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "usage: tmux-panes claude-session|save|restore <claude|emacs>" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in new issue
Block a user