1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# tmuxrdr roadmap: what only patching herdr can do
What is left of tmux's default prefix table, and of this machine's tmux.conf,
after `tmuxrdr/config/config.toml` and the tmux-mode plugin (`tmuxrdr/plugin`).
Each gap: what tmux does, why neither config nor plugin reaches it, and the
patch -- with touch points checked against the herdr v0.9.3 source, the base
this branch sits on. Each becomes one commit on the `tmuxrdr` branch, kept
small for rebasing (README.md, "Maintaining").
Applied so far: tmuxrdr's own directories -- `app_dir_name()` in
`src/config/io.rs` returns `tmuxrdr` -- so it runs beside stock herdr; and
tmux's status line as the tab row (`ui.tab_style = "tmux"`,
`src/client/shell/tabs_tmux.rs`). None of the patches below is applied yet.
## Coverage
tmux 3.7c's default prefix table has 88 bindings (`tmux -L x -f /dev/null
start-server \; list-keys -T prefix`). With this machine's tmux.conf on top:
- **67 bound** in config or plugin, each pressed in a tmuxrdr client (run in a
throwaway tmux on its own socket) and its effect checked through the herdr
CLI -- except `M-n`/`M-p`, seen only to do no harm with no agents running,
and of `1`..`9` only 1 and 3. Approximate rather than exact: `w` `f` `q` (herdr's
navigator), `/` (help, filtered), `C` (settings), `M-n` `M-p` (agents, not
alerts), `E` (evens the whole tab, not just the current pane's
neighbours), the C-/M-arrow step sizes (fractions of the area, not cells),
and window indexes (positions -- see "Stable window indexes").
- **5 moot**: `S-Up` `S-Down` `S-Left` `S-Right` and `DC` pan or reset the view
of a client smaller than its window. Herdr always sizes a tab to its client.
- **16 need a fork**: `Space`, `M-1`..`M-7`, `<`, `>`, `#`, `=`, `-`, `D`,
`C-z`, `PPage` -- below, with the behaviours a binding cannot carry.
## Where patches go
Building, installing and rebasing are in README.md. For the patches below:
the keybinding layer is compact -- adding an action touches four files:
`src/config/keybinds.rs` (config field, parser), `src/input/keybindings.rs`
(`KeybindAction`, resolver table), `src/client/shell/input.rs` (mode
dispatch), `src/client/shell/actions.rs` (what it does). Put the logic in new
files where it can live apart, so the lines that touch upstream's stay few.
## Patches, smallest first
### 1. PageUp/PageDown/Home/End/Delete/Insert key names -- tmux `PPage`
- tmux: `prefix+PPage` is `copy-mode -u`, copy mode scrolled up a page.
- Gap: `parse_key_combo` (`src/config/keybinds.rs`, the `match lower.as_str()`
near line 1303) knows no `pageup`, so nothing can bind it -- upstream
issue #4892, "Keybinding will not accept PageUp", opened 2026-10-04. Copy
mode is client-side and not in the CLI, so no plugin can enter it either.
- Patch: six arms in that match (`"pageup" | "ppage" => KeyCode::PageUp`, ...);
a `copy_mode_page_up` action that enters copy mode and applies the page-up
path `src/client/shell/copy_mode.rs` already has (`KeyCode::PageUp`, line
161). Bind `prefix+pageup`. Small; the key names alone are a likely-accepted
upstream PR.
### 2. Repeatable bindings -- tmux `-r`, repeat-time 500 ms
- tmux: arrows (select-pane), C-arrows and M-arrows (resize) repeat --
prefix once, then the arrow as often as wanted within 500 ms of the last.
- Gap: `src/client/shell/input.rs`, the `ClientShellMode::Prefix` arm, sets
`self.mode = return_mode` before running any binding: prefix mode is one
key, always. herdr's resize mode (`prefix+ctrl+r` here) is the nearest.
- Patch: a `repeat` flag on bindings (config: a `keys.repeatable` list of
action names, plus `repeat = true` on `[[keys.command]]` for the C-arrow
commands) and `keys.repeat_time` (ms, default 500). After a repeatable
binding, stay in Prefix with `prefix_repeat_deadline = now + repeat_time`;
on the next key past the deadline, drop to terminal mode and route the key
as typed -- tmux checks on the key too, so no timer is needed. Small to
medium.
### 3. synchronize-panes -- tmux.conf's ``bind ` setw synchronize-panes``
- tmux: every pane of the window gets the keys typed into one.
- Gap: no API takes input once for many panes, and input routing is in the
client. Upstream discussion "Hotkey / Toogle to Synchronize Input to All
Panes in a Workspace" asks for exactly this.
- Patch: a per-tab `synchronized` flag and a `toggle_synchronize_panes`
action; in `input.rs`'s Terminal arm, where keys go to
`self.focused_pane_id().map(ClientInputTarget::Pane)` (line 577), fan out
to every pane of the tab while the flag is set; mark it in the tab bar.
Small to medium.
### 4. C-z suspend-client
- tmux: stops the client (SIGTSTP) and returns to the shell that started it;
`fg` brings it back.
- Gap: client-only; nothing in the client handles it (no SIGTSTP in
`src/client`).
- Patch: an action that leaves the alternate screen and raw mode, raises
SIGTSTP, and on SIGCONT re-enters them and forces a full redraw. Small.
(Under `exec herdr` from a terminal emulator there is no shell to return
to -- tmux has the same problem -- so this matters only for herdr started
by hand.)
### 5. Stable window indexes -- tmux's renumber-windows off
- tmux: closing window 2 of 3 leaves 1 and 3; prefix+3 still reaches the
third.
- Gap: herdr shows an unnamed tab's place (`tab_display_name`,
`src/workspace.rs` line 442: `tab_idx + 1`) and switch_tab counts places
(`src/client/shell/actions.rs` line 955), so tabs renumber on every close
-- tmux with renumber-windows on.
- Patch: a `ui.stable_tab_numbers` option showing each tab's public number
(already kept: `next_public_tab_number`) and making switch_tab, `0` and `'`
select by it. Small. Optional -- many tmux users set renumber-windows on.
### 6. `<` `>` window and pane menus
- tmux: prefix+< and prefix+> open the window and pane menus.
- Gap: herdr's menus are right-click only (`src/client/shell/context_menu.rs`);
no action opens one from the keyboard.
- Patch: `open_tab_menu` and `open_pane_menu` actions opening the existing
menus anchored on the focused tab and pane. Small to medium.
### 7. Marked-pane border
- tmux: the marked pane's border is highlighted.
- Gap: plugins cannot style borders; tmux-mode toasts "pane N marked"
instead.
- Patch: a `marked` pane flag settable over the socket, drawn by the border
rendering in `src/ui/panes.rs`; the plugin then sets it. Small.
### 8. Layouts -- `Space`, `M-1`..`M-7`
- tmux: next-layout and select-layout even-horizontal, even-vertical,
main-horizontal, main-vertical, tiled, and the two mirrored mains, with
main-pane-width 80 and main-pane-height 24.
- Gap: a tab is a BSP tree (`Node`, `src/layout.rs` line 73). The socket can
set split ratios (`layout.set_split_ratio`, which tmux-mode's `E` uses) but
not move a pane within its tab (`pane move` to the same tab reports
`changed: false`), and `layout.apply` replaces the tab and kills its
processes. A plugin could round-trip every pane through temporary tabs
(`pane move` keeps a pane's process) and rebuild the tree -- it works on
paper, flickers through tab creation in practice, and is not done here.
- Patch: `Layout::apply_preset(preset, ordered_panes)` in `src/layout.rs`
rebuilding the tree from the pane order -- panes are ids in the tree, so
their processes are untouched -- with the seven presets and tmux's
main-pane sizes; `select_layout_*` and `next_layout` actions (per-tab index
for the cycle). Medium.
### 9. display-panes -- `q`
- tmux: numbers on every pane for display-panes-time (1 s); a digit picks one.
- Gap: needs an overlay over the panes and a timed input mode. herdr's
navigator stands in on `q`.
- Patch: a `DisplayPanes` client mode with a deadline, the numbers drawn by
`src/client/shell/render.rs`, digits focusing the pane. Medium.
### 10. Paste buffers -- `#` `=` `-`
- tmux: a stack of copied buffers (buffer-limit 50); list-buffers,
choose-buffer, delete-buffer.
- Gap: herdr writes copies straight to the system clipboard
(`write_clipboard`, `src/platform/macos.rs` line 631), and plugins see no
copy event, so nothing can keep a history. tmux-mode's `]` pastes the
clipboard, which is tmux's newest buffer.
- Patch: push each copy onto a capped buffer list as it is written; a buffer
view for `#`, a picker for `=` that pastes through the bracketed-paste path
`pane send-text` already uses (`encode_api_text`,
`src/app/api_helpers.rs`), delete for `-`. Medium.
### 11. A mouse drag enters copy mode
- tmux: a drag enters copy mode (`MouseDrag1Pane` -> `copy-mode -M`); this
machine's tmux.conf unbinds `copy-mode-vi MouseDragEnd1Pane`, so the
selection stays there for `y` (its copy-pipe binding), `Enter`, or more
vi movement; double- and triple-click do the same with a word or a line.
- Gap: herdr's mouse selection is separate from copy mode (`mouse.rs`): kept
(copy_on_select = false) it copies only on Ctrl+C, and `y` goes to the
pane. tmuxrdr's config copies on release meanwhile.
- Patch: on drag release (and double/triple click), enter copy mode for that
pane with the selection carried over -- copy mode's selection is an anchor
and a cursor (`ClientCopySelection::Character`), in the same absolute pane
coordinates as the mouse selection (`sync_copy_selection`). Logic in a
tmuxrdr file, hooked from `mouse.rs` and `word_selection.rs`. Small to
medium.
### 12. `D` choose-client
- tmux: lists attached clients; pick one to detach.
- Gap: no client list in the CLI or UI.
- Patch: a client registry query on the server and a picker. Medium; low
value on a single-user machine.
|