fix(zellij): repair scrollback logging on shell exit

The zellij wrapper (functions/zellij.fish) rewrote every `zellij`
invocation as `zellij options --theme catppuccin-mocha <args>`, which is
only valid for launching a new session. This mangled every subcommand,
including the `zellij action dump-screen` call inside _zellij_dump_log,
so no logs were ever produced. The theme is already set in config.kdl,
making the wrapper redundant — remove it.

Also harden _zellij_dump_log:
- dump-screen takes the file via stdout redirect, not a positional arg
  (rejected by zellij 0.44) nor --path (server-side write, flaky)
- add --ansi to preserve color in the logs
- discard empty dumps instead of leaving junk files

Document the structural limitation: zellij can only snapshot on a clean
shell exit, unlike tmux's continuous pipe-pane stream. Closing a pane or
quitting zellij tears down the pane/server before it can be dumped.
This commit is contained in:
2026-06-16 15:05:41 -04:00
parent 548df81c08
commit 7564604c53
5 changed files with 38 additions and 33 deletions
+6 -1
View File
@@ -66,13 +66,18 @@ This config captures terminal output to `~/.terminal_history` (override with
|---|---|---|
| Kitty scrollback | When a Kitty window/tab closes | `scrollback_<timestamp>.log` |
| tmux pane | Continuously while the pane is open (`pipe-pane`) | `tmux_<session>-w<win>-p<pane>_<timestamp>.log` |
| zellij pane | Snapshot taken on shell exit (`dump-screen`) | `zellij_<session>-p<pane>_<timestamp>.log` |
| zellij pane | Snapshot taken on **clean** shell exit (`dump-screen`) | `zellij_<session>-p<pane>_<timestamp>.log` |
| `paru` wrapper | Every `paru` invocation | `paru_<timestamp>.log` |
| `yay` wrapper | Every `yay` invocation | `yay_<timestamp>.log` |
Old logs are pruned automatically to stay within `$SCROLLBACK_HISTORY_MAX_FILES`
(default 100) per source, and empty/trivial captures are discarded.
> **zellij caveat:** zellij has no continuous pipe like tmux, so its pane is
> snapshotted only on a clean shell exit (`exit`/Ctrl-D). Closing a pane or
> quitting zellij directly tears down the pane/server before it can be dumped,
> so those sessions are not logged. End with `exit` to guarantee a log.
**These logs can contain secrets** — anything printed to your terminal (command
output, file dumps, tokens echoed to stdout) ends up in them. They never leave
your machine, but treat `~/.terminal_history` as sensitive.
-1
View File
@@ -155,7 +155,6 @@ joplin=### joplin
replay=### replay
tmux=### tmux-clean
wake-lock=### wake-lock
zellij=### zellij
# ── Section 6: Dependency Catalog ────────────────────────────
catalog=# 6. DEPENDENCY CATALOG
+26 -11
View File
@@ -1423,11 +1423,6 @@ Add -i (interactive confirmation) to destructive commands:
wake-lock rsync -avz src/ dest/
### zellij
Synopsis: zellij [args...]
Launches zellij with the Catppuccin Mocha theme applied.
---
# 6. DEPENDENCY CATALOG
@@ -1750,12 +1745,32 @@ SCROLLBACK_HISTORY_MAX_FILES, matching the paru/yay wrapper behaviour.
The zellij capture works differently: Zellij has no live output-streaming
facility like pipe-pane, so the log is taken as a one-shot snapshot when the
shell exits, via `zellij action dump-screen --full`. A fish_exit handler
(registered whenever $ZELLIJ is set) writes the pane's full scrollback and
then prunes old zellij_*.log files the same way. Because the capture happens
at exit, toggling __fish_config_op_logging takes effect on the next exit with
no restart or sentinel coordination needed — the C5 guard is re-checked when
the handler fires.
shell exits, via `zellij action dump-screen --full --ansi` (the --ansi flag
preserves color). The dump is captured on the fish process's stdout and
written to the log file by fish itself (not via `--path`, which would make the
zellij server write the file). A fish_exit handler (registered whenever
$ZELLIJ is set) writes the pane's full scrollback and then prunes old
zellij_*.log files the same way. Because the capture happens at exit, toggling
__fish_config_op_logging takes effect on the next exit with no restart or
sentinel coordination needed — the C5 guard is re-checked when the handler
fires.
LIMITATION — zellij capture only fires on a clean shell exit (typing `exit`,
Ctrl-D, or a logout), because that is when the fish_exit handler runs. It does
NOT capture when you close a pane or quit zellij through zellij itself:
- Closing a pane signals the shell and tears the pane down concurrently, so
even if the handler runs, `dump-screen` may find the pane buffer already
gone.
- Quitting zellij kills the zellij server, and `dump-screen` needs a live
server to read from — there is nothing left to snapshot.
This is a structural difference from tmux, NOT a bug. tmux streams pane output
to disk continuously via pipe-pane, so whatever was printed is already saved
no matter how the pane dies. Zellij can only snapshot, and the only reliable
snapshot point from the shell is a clean exit. To guarantee a zellij pane is
logged, end the session with `exit` or Ctrl-D rather than zellij's close-pane
or quit actions.
The Kitty watcher is managed by the kitty-logging command: it installs a
version-marked watcher (fish-config-watcher.py) into the Kitty config directory
+6 -1
View File
@@ -34,7 +34,12 @@ function _zellij_dump_log --description 'Dump the current Zellij pane scrollback
set -l log_file "$log_dir/zellij_"$session"-p"$pane_id"_"$timestamp".log"
mkdir -p $log_dir
zellij action dump-screen --full "$log_file" 2>/dev/null
# Dump to STDOUT and let this fish process write the file. `--path` makes the
# zellij *server* write the file (its CWD/permissions, historically flaky);
# capturing STDOUT keeps the write client-side and reliable. Drop the empty
# file if the dump produced nothing (e.g. pane already torn down on exit).
zellij action dump-screen --full --ansi >"$log_file" 2>/dev/null
test -s "$log_file"; or command rm -f "$log_file"
_prune_terminal_logs zellij
end
-19
View File
@@ -1,19 +0,0 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# zellij [args...]
#
# DESCRIPTION
# Launches zellij with the Catppuccin Mocha theme applied via
# --theme catppuccin-mocha.
#
# ARGUMENTS
# args... Arguments forwarded to zellij
#
# EXAMPLE
# zellij
function zellij --wraps='zellij' --description 'alias zellij=zellij options --theme catppuccin-mocha'
command zellij options --theme catppuccin-mocha $argv
end