<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/eshell-vterm-config.el, branch main</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-10T08:25:35+00:00</updated>
<entry>
<title>refactor: split eshell-vterm-config into eshell-config and vterm-config</title>
<updated>2026-05-10T08:25:35+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T08:25:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=b099c779343119ce40f2636469e118ec104002e0'/>
<id>urn:sha1:b099c779343119ce40f2636469e118ec104002e0</id>
<content type='text'>
The combined module had grown to 573 lines covering two unrelated subsystems with no shared state — the eshell shell-mode commands and the vterm/F12 toggle. The header even rendered this with two `;; ----` dividers. Split into two focused modules. eshell-config.el keeps the eshell user commands and package wiring (~170 lines). vterm-config.el keeps the vterm package, the tmux history capture command, the F12 toggle, and the C-; V keymap (~400 lines). Update init.el to require both, point the four vterm test files at vterm-config, and refresh the cross-module commentary in cj-window-geometry.el and cj-window-toggle.el.

No behavior change. Full test suite green; validate-modules clean.
</content>
</entry>
<entry>
<title>refactor: extract toggle-state helpers shared by F9 and F12</title>
<updated>2026-05-10T08:19:03+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T08:19:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=9712c2e122bd6923298910fcb53b33ca675ddd82'/>
<id>urn:sha1:9712c2e122bd6923298910fcb53b33ca675ddd82</id>
<content type='text'>
The F12 commit (554b32d) flagged this as a follow-up: ~120 lines of capture-state and display-saved logic were duplicated between modules/ai-vterm.el and modules/eshell-vterm-config.el. The only differences were the default direction (right for F9, below for F12) and the customization name for the fallback size. Extract the shared logic into modules/cj-window-toggle.el so both consumers reduce to thin delegates that pass their state-var symbols and defaults. The state vars stay where they were, so existing tests against each consumer's helpers keep working.

10 new tests cover the parameterized helpers in isolation. All consumer tests still pass.
</content>
</entry>
<entry>
<title>chore(vterm): fix docstring quoting and drop unused alias</title>
<updated>2026-05-10T08:13:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T08:13:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=b4f2b1d7d18f9246b06baf1e573b2cd990af83c8'/>
<id>urn:sha1:b4f2b1d7d18f9246b06baf1e573b2cd990af83c8</id>
<content type='text'>
Quote the symbol name `below' in three F12 toggle docstrings so the byte-compiler stops flagging the unescaped single quotes. Same change in each: `'below` becomes `\`below'` so Emacs renders it as a code reference. Also drop the unused `cj/vterm-history` alias — no callers in the tree.
</content>
</entry>
<entry>
<title>Add Emacs-native vterm copy workflows</title>
<updated>2026-05-10T07:44:21+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-10T07:44:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=fe937e752243107bead6e7711dea5a231a853d92'/>
<id>urn:sha1:fe937e752243107bead6e7711dea5a231a853d92</id>
<content type='text'>
Add an Emacs-first copy workflow for vterm and tmux. C-; V c enters vterm copy mode, C-; V C captures tmux pane scrollback into a temporary Emacs buffer, M-w copies and returns, and C-g/Escape cancel without copying. This also adds clickable URLs, removes the bad vtermf binding, unbinds C-c C-t, and tests the vterm/tmux keymap behavior.
</content>
</entry>
<entry>
<title>refactor: extract window-geometry helpers shared by F9 and F12</title>
<updated>2026-05-09T19:48:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-09T19:48:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=7ad613f96380319c037f367a1b6b1beda03846ca'/>
<id>urn:sha1:7ad613f96380319c037f367a1b6b1beda03846ca</id>
<content type='text'>
`ai-vterm.el` (F9) and `eshell-vterm-config.el` (F12) both grew the same geometry-preservation pattern: classify a window's position, capture its body size, map cardinal direction to its frame-edge variant. The shared helpers were sitting as near-duplicates in both modules. With two real consumers established, the abstraction has the right shape. I pulled them into `cj-window-geometry.el`.

The new module exposes three pure helpers:

- `cj/window-direction` returns right/below/left/above based on edges relative to `frame-root-window`. Takes an optional DEFAULT for the single-window-frame fallback so each consumer picks its own (ai-vterm wants 'right, vterm-toggle wants 'below).
- `cj/window-body-size` returns body-cols (right/left) or body-lines (below/above). Same body-vs-total reasoning as before: divider-independent, matches what the user sees.
- `cj/cardinal-to-edge-direction` maps right/left/below/above to rightmost/leftmost/bottom/top, used by each consumer's `display-saved` action.

`ai-vterm.el` and `eshell-vterm-config.el` now `(require 'cj-window-geometry)` and call the shared helpers directly. The consumer-specific `capture-state` and `display-saved` bodies stay in each module because they bind to consumer-specific state vars. Extracting those would either need parameter-passing-via-symbol or a macro, both heavier than the duplication they would remove.

Tests: 15 in `test-cj-window-geometry.el` covering all four directions, body-size on both axes, cardinal-to-edge mapping, default-arg fallback, and the unknown-direction nil case. Deleted `test-ai-vterm--window-geometry.el` (now redundant) and trimmed four duplicate window-direction/size tests from `test-vterm-toggle--display.el`. Net LOC: each consumer ~40-50 lines lighter, with the new module + tests paying roughly half that back. Full make test green. make validate-modules green.
</content>
</entry>
<entry>
<title>feat(vterm): F12 toggle that excludes claude and preserves geometry</title>
<updated>2026-05-09T16:58:52+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-09T16:58:52+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=554b32db5da9630fa24fb2abf3f93f21b03ff7a0'/>
<id>urn:sha1:554b32db5da9630fa24fb2abf3f93f21b03ff7a0</id>
<content type='text'>
vterm-toggle picked the most-recently-selected vterm buffer as F12's toggle target. After using F9 on a claude vterm, the most-recent vterm IS claude, so F12 ended up toggling claude, which has its own F9 / C-F9 / M-F9 surface in ai-vterm.el and shouldn't be affected. The display rule also had a hard-coded `(window-height . 0.7)` that overrode mouse-resize and orientation flips on every toggle.

I replaced the F12 binding with `cj/vterm-toggle` in `eshell-vterm-config.el`, mirroring the pattern shipped in ai-vterm.el:

- `cj/--vterm-toggle-buffer-p` excludes claude-prefixed buffers from F12's candidate set.
- `cj/--vterm-toggle-capture-state` records direction + body size at toggle-off.
- `cj/--vterm-toggle-display-saved` replays via `(body-columns . N)` / `(body-lines . N)` cons forms with the cardinal direction mapped to its frame-edge variant (`right` -&gt; `rightmost`, `below` -&gt; `bottom`, etc.) so vterm always lands at the captured edge regardless of selected window.
- `cj/vterm-toggle` uses `delete-window` (with `one-window-p` guard) on toggle-off so buffer-move scenarios don't leak ghost windows.

Default direction is `'below` to match F12's traditional bottom split. The vterm-toggle package stays installed so `M-x vterm-toggle` still works. Only the F12 binding changes.

19 new tests across three files: buffer-filter, dispatch, display. Full make test green.

Tradeoff: ~150 lines of geometry helpers and capture/display action logic are duplicated from ai-vterm.el. Worth extracting into a shared module now that two consumers exist. I filed it as a follow-up rather than blocking this ship.
</content>
</entry>
<entry>
<title>fix(ai-vterm): direction-based display + per-project tmux session names</title>
<updated>2026-05-08T03:09:15+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-08T03:09:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1d93e1a6569e4193c2b078a3d5df0bf47eeba9df'/>
<id>urn:sha1:1d93e1a6569e4193c2b078a3d5df0bf47eeba9df</id>
<content type='text'>
Two post-ship issues blocked practical use of the new launcher.

The display rule used `display-buffer-in-side-window` with `(dedicated . t)`. Side-window dedication caused `set-window-buffer` to error during `buffer-move` (C-M-arrows), which left a half-finished swap with both sides showing the claude buffer. Then `switch-to-buffer` on a non-claude buffer in that dedicated window split instead of replacing.

I rewrote the rule as `display-buffer-reuse-window -&gt; display-buffer-use-some-window -&gt; display-buffer-in-direction (right)`. The resulting window is ordinary, not dedicated, so swap and replace work normally. I also narrowed `vterm-toggle`'s broad lambda (which matches any vterm-mode buffer) to exclude `claude [` buffers. Otherwise vterm-toggle's `:defer` made it install last and capture our buffers first with its own bottom-split + dedicated treatment.

The tmux side: vterm's auto-launch hook ran a bare `tmux\n`, so each session got an auto-named one. After an Emacs crash the tmux session would survive but I couldn't find it. A second F9 just spawned another. The launcher now sends `tmux new-session -A -s &lt;basename&gt; -c &lt;dir&gt; '&lt;claude&gt;; exec bash'`. The `-A` reattaches to a same-named session if it already exists. The `exec bash` keeps the tmux window alive if claude itself exits. A `cj/--ai-vterm-suppress-tmux` flag tells the existing vterm hook to skip its bare tmux step so the named launch runs instead.

11 new tests across 2 files cover the session-name and launch-command helpers. I updated tests for show-or-create and the display rule. All 34 ai-vterm tests are green.
</content>
</entry>
<entry>
<title>feat(ai-vterm): add Claude launcher with vertical-split vterm</title>
<updated>2026-05-08T00:25:18+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-08T00:25:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=47b218ed15acd00c18cbc3bef604c4f2e0050a08'/>
<id>urn:sha1:47b218ed15acd00c18cbc3bef604c4f2e0050a08</id>
<content type='text'>
The new module picks a Claude-template project from a filtered completing-read list. It scans the same roots the `ai` shell launcher uses, then opens or reuses a vterm buffer named `claude [&lt;repo&gt;]` on the right. F9 launches it. The prior `cj/toggle-gptel` binding moves from F9 to C-F9 so both AI tools share the same physical key.

The display rule chains reuse-window -&gt; use-some-window -&gt; in-direction (right). The resulting window isn't dedicated. That matters because side-window dedication was breaking `buffer-move` (C-M-arrows) and `switch-to-buffer` replacement on the claude buffer. I also narrowed `vterm-toggle`'s display rule to skip `claude [` buffers. Otherwise it claimed them first with its bottom-split + dedicated treatment.

I added 23 tests across 5 files: the buffer-name transform, candidate walker, show-or-create dispatch, picker, and display rule. Design lives at docs/design/ai-vterm.org.
</content>
</entry>
<entry>
<title>fix(eshell): correct call shape in eshell/find-using-dired</title>
<updated>2026-04-29T08:23:38+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-29T08:23:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d91be5e5d0c23f998779ea301f2fcb335590257f'/>
<id>urn:sha1:d91be5e5d0c23f998779ea301f2fcb335590257f</id>
<content type='text'>
The body had `(find-name-dired . escaped-pattern)`, a dotted pair instead of a function call. The reader accepts it, but the form crashes the moment the `f` alias runs. find-name-dired takes (DIR PATTERN), so the right shape passes default-directory and the escaped pattern.
</content>
</entry>
<entry>
<title>feat(vterm): pass F8, F9, F10 through to Emacs</title>
<updated>2026-04-07T18:04:48+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-07T18:04:48+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=04addf42cff28e0099264f3e9a83843ea50286b8'/>
<id>urn:sha1:04addf42cff28e0099264f3e9a83843ea50286b8</id>
<content type='text'>
Add nil bindings for F8 (agenda), F9 (gptel), and F10 (music) in
vterm-mode-map so these global keybindings aren't swallowed by vterm.
Also disable wttrin-debug and update abbreviations.
</content>
</entry>
</feed>
