aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tmuxrdr/README.md108
-rw-r--r--tmuxrdr/ROADMAP.md152
2 files changed, 260 insertions, 0 deletions
diff --git a/tmuxrdr/README.md b/tmuxrdr/README.md
new file mode 100644
index 00000000..b152b39e
--- /dev/null
+++ b/tmuxrdr/README.md
@@ -0,0 +1,108 @@
+# tmuxrdr
+
+herdr without the disrespect for tmux users.
+
+[herdr](https://herdr.dev) is a terminal workspace manager for AI coding
+agents: tmux's model -- a server owning panes, clients attaching -- plus an
+agent-aware sidebar. Its keys are its own. tmuxrdr is herdr with tmux's keys
+and tmux's behaviour wherever herdr can be made to have them: tmux 3.7c's
+default prefix table, with this machine's `dot/config/tmux.conf` and
+`~/.tmux.conf.local` on top.
+
+Everything tmuxrdr adds is in this directory. The rest of the checkout is
+herdr, untouched, on a herdr release tag.
+
+| Path | What |
+|---|---|
+| `config/config.toml` | herdr config: tmux's bindings on herdr's actions, each commented with the tmux line it mirrors |
+| `plugin/` | the tmux-mode herdr plugin: the bindings herdr has no action for -- break-pane, swap/rotate by pane order, marks, last session, select/move-window, paste-buffer, display-message, select-layout -E, kill with y/n, the `:` prompt, clock, messages, automatic-rename |
+| `scripts/install` | puts this machine on the config and plugin |
+| `scripts/test-e2e` | presses every binding in a real herdr client and checks the result |
+| `scripts/sync-upstream` | rebases tmuxrdr onto a newer herdr release |
+| `ROADMAP.md` | what only a patch to herdr's code can do, with the patch for each |
+
+## Status
+
+- Based on herdr **v0.9.3**, the release Homebrew ships.
+- Of tmux's 88 default prefix bindings, **67 work** (some approximately --
+ ROADMAP.md, "Coverage"), **5 are moot** in herdr, **16 need code**.
+ `scripts/test-e2e`: 66 checks, all passing on v0.9.3.
+- **No herdr code is patched yet.** The config and plugin run on stock herdr;
+ the first code patches are in ROADMAP.md, smallest first.
+
+## Install
+
+```sh
+tmuxrdr/scripts/install # dry run: what it would do
+tmuxrdr/scripts/install yes
+```
+
+It links `~/.config/herdr/config.toml` to `config/config.toml`, keeping a real
+file already there as `config.toml.pre-tmuxrdr`, and
+`~/.local/share/tmuxrdr/plugin` to `plugin/`, which it registers with
+`herdr plugin link`. herdr saves its settings in place, so changes made from
+its settings screen land in `config/config.toml` too -- `git diff` shows them.
+Then, in a running herdr: `prefix+r`.
+
+Needs `jq`. Run it again after moving the checkout.
+
+To leave: `herdr plugin unlink tmuxrdr.tmux-mode`, remove the two links, and
+move `config.toml.pre-tmuxrdr` back if you want herdr's old config.
+
+## Test
+
+```sh
+tmuxrdr/scripts/test-e2e
+```
+
+About a minute. It runs a herdr client inside a throwaway tmux server (its own
+socket, its prefix off), on a throwaway herdr session and a copy of the
+config, presses each binding, checks the effect through the herdr CLI, and
+removes everything after. Your sessions and config are not touched. `HERDR=`
+tests another binary -- a tmuxrdr build, once there is one.
+
+## Maintaining
+
+The fork is meant to follow herdr release by release with as little work as
+possible. What keeps it that way:
+
+- **One branch, `tmuxrdr`:** a herdr release tag with tmuxrdr's commits on
+ top. Remote `upstream` is herdr, fetch-only -- its push URL is disabled.
+- **Fork content stays in `tmuxrdr/`.** Upstream never touches this
+ directory, so these commits never conflict. Do not edit upstream files for
+ anything that can live here -- not even the top-level README.
+- **Code patches are one commit each, and small.** Put the logic in new
+ files; keep the lines changed in upstream's files to the hooks that call
+ it. ROADMAP.md says where each patch touches.
+- **rerere is on** in this checkout (`git config rerere.enabled true`), so a
+ conflict resolved once is resolved by itself next time.
+
+To move to a new herdr release:
+
+```sh
+tmuxrdr/scripts/sync-upstream # or: sync-upstream v0.9.4
+tmuxrdr/scripts/test-e2e
+```
+
+`sync-upstream` keeps the branch as it was under `tmuxrdr-on-<old tag>`; if
+something is wrong, `git switch -C tmuxrdr tmuxrdr-on-<old tag>` puts it back.
+Test against the new release: upgrade the herdr on PATH (`brew upgrade
+herdr`) while there are no code patches, or build this checkout once there
+are.
+
+## Building
+
+Not needed until the first code patch: until then, tmuxrdr runs on stock
+herdr. When it is:
+
+- herdr pins Rust 1.96.1 in `rust-toolchain.toml`. Install rustup with
+ `--no-modify-path` -- dot's shell profiles are links into a repo -- and put
+ `. "$HOME/.cargo/env"` in `~/.localenv`.
+- Fetch crates through Socket Firewall: `sfw cargo build --release`.
+- Client and server are one binary and check each other's protocol version.
+ Stop the server (`herdr server stop`) before switching, then install the
+ build as `~/.local/bin/herdr`, which comes before Homebrew's on PATH here.
+ Keep it named `herdr`: agent integrations call that name.
+- Never `herdr update` a tmuxrdr build -- it installs stock herdr over it.
+
+License: Apache-2.0, like herdr (`LICENSE` at the top of the checkout).
diff --git a/tmuxrdr/ROADMAP.md b/tmuxrdr/ROADMAP.md
new file mode 100644
index 00000000..91dba8ee
--- /dev/null
+++ b/tmuxrdr/ROADMAP.md
@@ -0,0 +1,152 @@
+# 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. None is applied yet; each becomes one commit on the
+`tmuxrdr` branch, kept small for rebasing (README.md, "Maintaining").
+
+## 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 herdr 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. `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.