diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-25 04:10:38 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-25 04:10:38 -0500 |
| commit | 3f75b39bbbc4e1c136d3f786024c5c1ed19011ce (patch) | |
| tree | c03cfe18f7a067cae026db400e757cbd5d053d35 /modules/cj-window-geometry-lib.el | |
| parent | 08014b2f15e099a1c5e662a17a41290f37aeebf4 (diff) | |
| download | dotemacs-3f75b39bbbc4e1c136d3f786024c5c1ed19011ce.tar.gz dotemacs-3f75b39bbbc4e1c136d3f786024c5c1ed19011ce.zip | |
fix(ai-vterm): reuse the frame's half instead of splitting a third
F9 split a third window into a frame that was already divided in two, wedging the agent into the middle or a skinny extra column instead of taking the half it should occupy. The display rule only knew how to reuse a window already showing an agent or to split a fresh one. With a plain two-pane layout it fell through to the split and added a window.
I added a display action, cj/--ai-vterm-reuse-edge-window, that reuses the window already forming the target half (the right column on a desktop, the bottom row on a laptop), found by a new cj/window-at-edge helper. It records the displaced buffer with display-buffer-record-window, so toggling off restores that buffer through the native quit-restore-window. The slot's buffer swaps between the agent and whatever it displaced, and no window is created or deleted. The split path still handles a single-window frame or a layout split on the other axis, and the lone fullscreen agent keeps its bury-and-restore-in-place behavior.
Diffstat (limited to 'modules/cj-window-geometry-lib.el')
| -rw-r--r-- | modules/cj-window-geometry-lib.el | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/modules/cj-window-geometry-lib.el b/modules/cj-window-geometry-lib.el index a674a072..1c1ab9fd 100644 --- a/modules/cj-window-geometry-lib.el +++ b/modules/cj-window-geometry-lib.el @@ -77,5 +77,40 @@ layouts." ('above 'top) (_ nil))) +(defun cj/window-at-edge (direction &optional frame) + "Return the window forming FRAME's half on the side named by DIRECTION. + +DIRECTION is one of right, left, below, above. A window qualifies as +the half when it sits flush against that frame edge and spans the full +perpendicular extent: for right or left it is a full-height side column +(nothing above, below, or further toward the edge, but a sibling on the +far side); for below or above it is a full-width row. FRAME defaults to +the selected frame. + +Existence is tested with `window-in-direction' rather than raw edge +arithmetic -- frame-internal geometry makes the root window's edges +disagree with its children's by a row, so an `=' comparison against the +root edges never matches. Asking \"is there a window on each side?\" +sidesteps that. + +Returns nil when no window qualifies: a single-window frame (no sibling +on the far side, so not a distinct half), an axis mismatch (a top/bottom +split when DIRECTION is right has no full-height right column), or a +nested edge no one window fills. The caller then falls back to splitting +a fresh half." + (when (memq direction '(right left below above)) + (let ((far (pcase direction + ('right 'left) ('left 'right) ('below 'above) ('above 'below))) + (perp (pcase direction + ((or 'right 'left) '(above below)) + ((or 'below 'above) '(left right))))) + (seq-find + (lambda (w) + (and (window-in-direction far w) + (not (window-in-direction direction w)) + (not (window-in-direction (nth 0 perp) w)) + (not (window-in-direction (nth 1 perp) w)))) + (window-list (or frame (selected-frame)) 'never))))) + (provide 'cj-window-geometry-lib) ;;; cj-window-geometry-lib.el ends here |
