diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9ba29..addb91a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Respect `eca-chat-window-side` (and width/height) even when `eca-chat-use-side-window` is nil, displaying the chat on that side via `display-buffer-in-direction`. To avoid overlapping/duplicate windows, placement now reuses the window already showing the chat, otherwise replaces another visible chat window in place (new-tab behavior), and only opens a new window when there is nothing to reuse. - Bugfix: keep the `:usage` and `:trust` mode-line segments visible after adding the context-usage bar. The right-alignment reserved space from `(length right)`, which counts the bar's pixel-width `display` spaces (and wide glyphs) as ~1 char each, so the segments overflowed off the right edge. It now measures the real rendered width via `string-pixel-width` and aligns flush to the right edge in pixels (Emacs 29+ only; on Emacs 28 right segments follow left without alignment). - Bugfix: closing a chat (`kill-buffer`, `C-c C-k`, or the tab close button) now switches the chat window to a sibling chat (the previous tab, or the only one left) instead of falling back to an unrelated buffer like the settings buffer, and drops the dead chat from the session registry. `C-c C-k` (`eca-chat-reset`) only starts a fresh chat when the closed chat was the last one. - Add `eca-chat-copy-at-point`, bound to `C-c C-w`. It copies the fenced code block at point, the assistant response at point, or the latest response as a fallback. diff --git a/README.md b/README.md index b540ef0..4dd996c 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,10 @@ Chat - `eca-chat-parent-mode`: Set major-mode of chat parent, can be `markdown-mode`, `markdown-view-mode` or `gfm-view-mode` (default) - `eca-chat-mode-hook`: Hooks to run after entering `eca-chat-mode`. - `eca-chat-finished-hook`: Hooks to run after finishing a chat prompt. -- `eca-chat-use-side-window`: Whether the chat buffer is displayed in a side window or a normal window. -- `eca-chat-window-side`: On which side (`left`, `right`, `top`, `bottom`) the chat side window appears. -- `eca-chat-window-width`: Width of the chat side window when on the left or right. -- `eca-chat-window-height`: Height of the chat side window when on the top or bottom. +- `eca-chat-use-side-window`: Whether the chat buffer is displayed in a dedicated side window or a normal window. Either way it is placed on `eca-chat-window-side`. +- `eca-chat-window-side`: On which side (`left`, `right`, `top`, `bottom`) the chat window appears (respected for both side and normal windows). +- `eca-chat-window-width`: Width of the chat window when on the left or right. +- `eca-chat-window-height`: Height of the chat window when on the top or bottom. - `eca-chat-focus-on-open`: Whether to focus the chat window when it opens. - `eca-chat-auto-add-repomap`: Whether to automatically include repoMap context when opening ECA. - `eca-chat-auto-add-cursor`: Whether to automatically track the cursor position and add it as context. diff --git a/eca-chat.el b/eca-chat.el index 693cdd4..82be991 100644 --- a/eca-chat.el +++ b/eca-chat.el @@ -57,8 +57,10 @@ such as the Doom workspaces tabline to refresh external indicators.") (defcustom eca-chat-window-side 'right "Side of the frame where the ECA chat window should appear. -Can be `'left', `'right', `'top', or `'bottom'. This setting will only -be used when `eca-chat-use-side-window' is non-nil." +Can be `'left', `'right', `'top', or `'bottom'. This setting is respected +both when `eca-chat-use-side-window' is non-nil (dedicated side window) +and when it is nil (regular window displayed in the corresponding +direction)." :type '(choice (const :tag "Left" left) (const :tag "Right" right) (const :tag "Top" top) @@ -76,11 +78,13 @@ be used when `eca-chat-use-side-window' is non-nil." :group 'eca) (defcustom eca-chat-use-side-window t - "Whether to display ECA chat in a side window. + "Whether to display ECA chat in a dedicated side window. When non-nil (default), ECA chat opens in a dedicated side window controlled by `eca-chat-window-side' and related settings. When nil, -ECA chat opens in a regular buffer that follows standard -`display-buffer' behavior." +ECA chat opens in a regular window, still placed on the side given by +`eca-chat-window-side' with the corresponding width/height, but without +the side-window restrictions (so it can be split or replaced like any +other window)." :type 'boolean :group 'eca) @@ -2642,30 +2646,42 @@ available, falling back to `string-width' on Emacsen without it." (select-window (get-buffer-window (buffer-name)))) (defun eca-chat--display-buffer (buffer) - "Display BUFFER in a side window according to customization. -The window is displayed on the side specified by -`eca-chat-window-side' with dimensions from -`eca-chat-window-width' or `eca-chat-window-height'. + "Display BUFFER according to customization. +BUFFER is displayed on the side given by `eca-chat-window-side' +with the size from `eca-chat-window-width' or +`eca-chat-window-height'. When `eca-chat-use-side-window' is +non-nil a dedicated side window is used, otherwise a regular +window in the same direction. If `eca-chat-focus-on-open' is non-nil, the window is selected." - (let ((window - (if eca-chat-use-side-window - ;; Use side window - (let* ((side eca-chat-window-side) - (slot 0) - (window-parameters '((no-delete-other-windows . t))) - (display-action - `((display-buffer-in-side-window) - (side . ,side) - (slot . ,slot) - (dedicated . side) - ,@(when (memq side '(left right)) - `((window-width . ,eca-chat-window-width))) - ,@(when (memq side '(top bottom)) - `((window-height . ,eca-chat-window-height))) - (window-parameters . ,window-parameters)))) - (display-buffer buffer display-action)) - ;; Use regular buffer - (display-buffer buffer)))) + (let* ((side eca-chat-window-side) + (size (if (memq side '(left right)) + `((window-width . ,eca-chat-window-width)) + `((window-height . ,eca-chat-window-height)))) + (display-action + (if eca-chat-use-side-window + ;; Dedicated side window. + `((display-buffer-in-side-window) + (side . ,side) + (slot . 0) + (dedicated . side) + ,@size + (window-parameters . ((no-delete-other-windows . t)))) + ;; Regular window in the same direction. + `((display-buffer-in-direction) + (direction . ,(pcase side ('top 'above) ('bottom 'below) (_ side))) + ,@size))) + (window + (or + ;; Already visible: keep it where it is. + (get-buffer-window buffer) + ;; Another chat is visible: replace it (new-tab behavior). + (when-let* ((win (get-window-with-predicate + (lambda (w) + (buffer-local-value 'eca-chat--id (window-buffer w)))))) + (set-window-buffer win buffer) + win) + ;; Nothing to reuse: open a new window. + (display-buffer buffer display-action)))) ;; Select the window to give it focus if configured to do so (when (and window eca-chat-focus-on-open) (select-window window))