branch: externals/ellama
commit 1c52902d4de4933b61cd3a3bcdd3f74cadd31f9f
Merge: 1b71217434 22dcffff59
Author: Sergey Kostyaev <s-kosty...@users.noreply.github.com>
Commit: GitHub <nore...@github.com>

    Merge pull request #235 from s-kostyaev/add-context-header-line
    
    Add ellama context header and mode line modes
---
 NEWS.org             |   8 ++
 README.org           | 215 ++++++++++++++++++++++++++++----------------
 docs/changelog.org   |   3 +
 ellama.el            | 245 +++++++++++++++++++++++++++++++++------------------
 tests/test-ellama.el |  61 +++++--------
 5 files changed, 334 insertions(+), 198 deletions(-)

diff --git a/NEWS.org b/NEWS.org
index e7b76e2678..9ec0c38ba8 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -1,3 +1,11 @@
+* Version 1.3.0
+- Implemented ellama context header line and mode line features.
+- Added ~ellama-context-header-line-mode~, ~ellama-context-mode-line-mode~ and
+  its global versions.
+- Session renaming functionality improvements.
+- Improved session deletion.
+- Renamed ~ellama-session-remove~ to ~ellama-session-delete~.
+- Removed ~ellama-long-lines-length~ customization and related usage
 * Version 1.2.5
 - Fix scroll function.
 * Version 1.2.4
diff --git a/README.org b/README.org
index aa2f5a1580..1bda4fb572 100644
--- a/README.org
+++ b/README.org
@@ -32,72 +32,74 @@ Without any configuration, the first available ollama model 
will be used.
 You can customize ellama configuration like this:
 
 #+BEGIN_SRC  emacs-lisp
-(use-package ellama
-  :ensure t
-  :bind ("C-c e" . ellama-transient-main-menu)
-  :init
-  ;; setup key bindings
-  ;; (setopt ellama-keymap-prefix "C-c e")
-  ;; language you want ellama to translate to
-  (setopt ellama-language "German")
-  ;; could be llm-openai for example
-  (require 'llm-ollama)
-  (setopt ellama-provider
-         (make-llm-ollama
-          ;; this model should be pulled to use it
-          ;; value should be the same as you print in terminal during pull
-          :chat-model "llama3:8b-instruct-q8_0"
-          :embedding-model "nomic-embed-text"
-          :default-chat-non-standard-params '(("num_ctx" . 8192))))
-  (setopt ellama-summarization-provider
-         (make-llm-ollama
-          :chat-model "qwen2.5:3b"
-          :embedding-model "nomic-embed-text"
-          :default-chat-non-standard-params '(("num_ctx" . 32768))))
-  (setopt ellama-coding-provider
-         (make-llm-ollama
-          :chat-model "qwen2.5-coder:3b"
-          :embedding-model "nomic-embed-text"
-          :default-chat-non-standard-params '(("num_ctx" . 32768))))
-  ;; Predefined llm providers for interactive switching.
-  ;; You shouldn't add ollama providers here - it can be selected interactively
-  ;; without it. It is just example.
-  (setopt ellama-providers
-         '(("zephyr" . (make-llm-ollama
-                        :chat-model "zephyr:7b-beta-q6_K"
-                        :embedding-model "zephyr:7b-beta-q6_K"))
-           ("mistral" . (make-llm-ollama
-                         :chat-model "mistral:7b-instruct-v0.2-q6_K"
-                         :embedding-model "mistral:7b-instruct-v0.2-q6_K"))
-           ("mixtral" . (make-llm-ollama
-                         :chat-model "mixtral:8x7b-instruct-v0.1-q3_K_M-4k"
-                         :embedding-model 
"mixtral:8x7b-instruct-v0.1-q3_K_M-4k"))))
-  ;; Naming new sessions with llm
-  (setopt ellama-naming-provider
-         (make-llm-ollama
-          :chat-model "llama3:8b-instruct-q8_0"
-          :embedding-model "nomic-embed-text"
-          :default-chat-non-standard-params '(("stop" . ("\n")))))
-  (setopt ellama-naming-scheme 'ellama-generate-name-by-llm)
-  ;; Translation llm provider
-  (setopt ellama-translation-provider
-         (make-llm-ollama
-          :chat-model "qwen2.5:3b"
-          :embedding-model "nomic-embed-text"
-          :default-chat-non-standard-params
-          '(("num_ctx" . 32768))))
-  (setopt ellama-extraction-provider (make-llm-ollama
-                                     :chat-model 
"qwen2.5-coder:7b-instruct-q8_0"
-                                     :embedding-model "nomic-embed-text"
-                                     :default-chat-non-standard-params
-                                     '(("num_ctx" . 32768))))
-  ;; customize display buffer behaviour
-  ;; see ~(info "(elisp) Buffer Display Action Functions")~
-  (setopt ellama-chat-display-action-function #'display-buffer-full-frame)
-  (setopt ellama-instant-display-action-function #'display-buffer-at-bottom)
-  :config
-  ;; send last message in chat buffer with C-c C-c
-  (add-hook 'org-ctrl-c-ctrl-c-hook #'ellama-chat-send-last-message))
+  (use-package ellama
+    :ensure t
+    :bind ("C-c e" . ellama-transient-main-menu)
+    :init
+    ;; setup key bindings
+    ;; (setopt ellama-keymap-prefix "C-c e")
+    ;; language you want ellama to translate to
+    (setopt ellama-language "German")
+    ;; could be llm-openai for example
+    (require 'llm-ollama)
+    (setopt ellama-provider
+         (make-llm-ollama
+          ;; this model should be pulled to use it
+          ;; value should be the same as you print in terminal during pull
+          :chat-model "llama3:8b-instruct-q8_0"
+          :embedding-model "nomic-embed-text"
+          :default-chat-non-standard-params '(("num_ctx" . 8192))))
+    (setopt ellama-summarization-provider
+         (make-llm-ollama
+          :chat-model "qwen2.5:3b"
+          :embedding-model "nomic-embed-text"
+          :default-chat-non-standard-params '(("num_ctx" . 32768))))
+    (setopt ellama-coding-provider
+         (make-llm-ollama
+          :chat-model "qwen2.5-coder:3b"
+          :embedding-model "nomic-embed-text"
+          :default-chat-non-standard-params '(("num_ctx" . 32768))))
+    ;; Predefined llm providers for interactive switching.
+    ;; You shouldn't add ollama providers here - it can be selected 
interactively
+    ;; without it. It is just example.
+    (setopt ellama-providers
+         '(("zephyr" . (make-llm-ollama
+                        :chat-model "zephyr:7b-beta-q6_K"
+                        :embedding-model "zephyr:7b-beta-q6_K"))
+           ("mistral" . (make-llm-ollama
+                         :chat-model "mistral:7b-instruct-v0.2-q6_K"
+                         :embedding-model "mistral:7b-instruct-v0.2-q6_K"))
+           ("mixtral" . (make-llm-ollama
+                         :chat-model "mixtral:8x7b-instruct-v0.1-q3_K_M-4k"
+                         :embedding-model 
"mixtral:8x7b-instruct-v0.1-q3_K_M-4k"))))
+    ;; Naming new sessions with llm
+    (setopt ellama-naming-provider
+         (make-llm-ollama
+          :chat-model "llama3:8b-instruct-q8_0"
+          :embedding-model "nomic-embed-text"
+          :default-chat-non-standard-params '(("stop" . ("\n")))))
+    (setopt ellama-naming-scheme 'ellama-generate-name-by-llm)
+    ;; Translation llm provider
+    (setopt ellama-translation-provider
+         (make-llm-ollama
+          :chat-model "qwen2.5:3b"
+          :embedding-model "nomic-embed-text"
+          :default-chat-non-standard-params
+          '(("num_ctx" . 32768))))
+    (setopt ellama-extraction-provider (make-llm-ollama
+                                     :chat-model 
"qwen2.5-coder:7b-instruct-q8_0"
+                                     :embedding-model "nomic-embed-text"
+                                     :default-chat-non-standard-params
+                                     '(("num_ctx" . 32768))))
+    ;; customize display buffer behaviour
+    ;; see ~(info "(elisp) Buffer Display Action Functions")~
+    (setopt ellama-chat-display-action-function #'display-buffer-full-frame)
+    (setopt ellama-instant-display-action-function #'display-buffer-at-bottom)
+    :config
+    ;; show ellama context in header line in all buffers
+    (ellama-context-header-line-global-mode +1)
+    ;; send last message in chat buffer with C-c C-c
+    (add-hook 'org-ctrl-c-ctrl-c-hook #'ellama-chat-send-last-message))
 #+END_SRC
 
 ** Commands
@@ -240,9 +242,9 @@ as a specified format using Ellama.
 
 Load ellama session from file.
 
-*** ellama-session-remove
+*** ellama-session-delete
 
-Remove ellama session.
+Delete ellama session.
 
 *** ellama-session-switch
 
@@ -340,7 +342,7 @@ Ellama, using the ~ellama-keymap-prefix~ prefix (not set by 
default):
 | "s c"  | ellama-summarize-killring       | Summarize killring           |
 | "s l"  | ellama-load-session             | Session Load                 |
 | "s r"  | ellama-session-rename           | Session rename               |
-| "s d"  | ellama-session-remove           | Session delete               |
+| "s d"  | ellama-session-delete           | Delete delete                |
 | "s a"  | ellama-session-switch           | Session activate             |
 | "P"    | ellama-proofread                | Proofread                    |
 | "i w"  | ellama-improve-wording          | Improve wording              |
@@ -383,6 +385,7 @@ There are many supported providers: ~ollama~, ~open ai~, 
~vertex~,
 ~GPT4All~. For more information see 
[[https://elpa.gnu.org/packages/llm.html][llm documentation]].
 - ~ellama-providers~: association list of model llm providers with
   name as key.
+- ~ellama-spinner-enabled~: Enable spinner during text generation.
 - ~ellama-spinner-type~: Spinner type for ellama. Default type is
 ~progress-bar~.
 - ~ellama-ollama-binary~: Path to ollama binary.
@@ -401,9 +404,6 @@ argument generated text string.
 - ~ellama-sessions-directory~: Directory for saved ellama sessions.
 - ~ellama-major-mode~: Major mode for ellama commands. Org mode by
   default.
-- ~ellama-long-lines-length~: Long lines length for fill paragraph
-  call. Too low value can break generated code by splitting long
-  comment lines. Default value 100.
 - ~ellama-session-auto-save~: Automatically save ellama sessions if
   set. Enabled by default.
 - ~ellama-naming-scheme~: How to name new sessions.
@@ -427,7 +427,6 @@ argument generated text string.
 - ~ellama-context-poshandler~: Position handler for displaying context buffer.
   ~posframe-poshandler-frame-top-center~ will be used if not set.
 - ~ellama-context-border-width~: Border width for the context buffer.
-- ~ellama-context-element-padding-size~: Padding size for context elements.
 - ~ellama-session-remove-reasoning~: Remove internal reasoning from
   the session after ellama provide an answer. This can improve
   long-term communication with reasoning models. Enabled by default.
@@ -439,13 +438,81 @@ argument generated text string.
   ellama output to enhance the versatility of reasoning models across
   diverse applications.
 - ~ellama-context-posframe-enabled~: Enable showing posframe with
-  ellama context. Enabled by default.
+  ellama context.
 - ~ellama-manage-context-display-action-function~: Display action
   function for ~ellama-render-context~. Default value
   ~display-buffer-same-window~.
 - ~ellama-preview-context-element-display-action-function~: Display
   action function for ~ellama-preview-context-element~.
 
+** Minor modes
+
+*** ellama-context-header-line-mode
+
+*Description:*
+Toggle the Ellama Context header line mode. This minor mode updates the header 
line to display
+context-specific information.
+
+*Usage:*
+To enable or disable ~ellama-context-header-line-mode~, use the command:
+
+    M-x ellama-context-header-line-mode
+
+When enabled, this mode adds a hook to ~window-state-change-hook~ to update 
the header line whenever
+the window state changes. It also calls ~ellama-context-update-header-line~ to 
initialize the header
+line with context-specific information.
+
+When disabled, it removes the evaluation of ~(:eval (ellama-context-line))~ 
from
+~header-line-format~.
+
+*** ellama-context-header-line-global-mode
+
+*Description:*
+Globalized version of ~ellama-context-header-line-mode~. This mode ensures that
+~ellama-context-header-line-mode~ is enabled in all buffers.
+
+*Usage:*
+To enable or disable ~ellama-context-header-line-global-mode~, use the command:
+
+    M-x ellama-context-header-line-global-mode
+
+This globalized minor mode provides a convenient way to ensure that 
context-specific header line
+information is always available, regardless of the buffer being edited.
+
+*** ellama-context-mode-line-mode
+
+*Description:*
+Toggle the Ellama Context mode line mode. This minor mode updates the mode line
+to display context-specific information.
+
+*Usage:*
+To enable or disable ~ellama-context-mode-line-mode~, use the command:
+
+    M-x ellama-context-mode-line-mode
+
+When enabled, this mode adds a hook to ~window-state-change-hook~ to update the
+mode line whenever the window state changes. It also calls
+~ellama-context-update-mode-line~ to initialize the mode line with
+context-specific information.
+
+When disabled, it removes the evaluation of ~(:eval (ellama-context-line))~ 
from
+~mode-line-format~.
+
+*** ellama-context-mode-line-global-mode
+
+*Description:*
+Globalized version of ~ellama-context-mode-line-mode~. This mode ensures that
+~ellama-context-mode-line-mode~ is enabled in all buffers.
+
+*Usage:*
+To enable or disable ~ellama-context-mode-line-global-mode~, use the command:
+
+    M-x ellama-context-mode-line-global-mode
+
+This globalized minor mode provides a convenient way to ensure that
+context-specific mode line information is always available, regardless of the
+buffer being edited.
+
 ** Acknowledgments
 
 Thanks [[https://github.com/jmorganca][Jeffrey Morgan]] for excellent project 
[[https://github.com/jmorganca/ollama][ollama]]. This project
diff --git a/docs/changelog.org b/docs/changelog.org
new file mode 100644
index 0000000000..a713a2d381
--- /dev/null
+++ b/docs/changelog.org
@@ -0,0 +1,3 @@
+Based on this git log write short changelog in markdown list format. Do not add
+any headings or anknowledgements. Every changelog element should be ended with
+full stop.
diff --git a/ellama.el b/ellama.el
index 20c92023ed..e6171a1922 100644
--- a/ellama.el
+++ b/ellama.el
@@ -5,8 +5,8 @@
 ;; Author: Sergey Kostyaev <sskosty...@gmail.com>
 ;; URL: http://github.com/s-kostyaev/ellama
 ;; Keywords: help local tools
-;; Package-Requires: ((emacs "28.1") (llm "0.22.0") (spinner "1.7.4") 
(transient "0.7") (compat "29.1") (posframe "1.4.0"))
-;; Version: 1.2.5
+;; Package-Requires: ((emacs "28.1") (llm "0.22.0") (transient "0.7") (compat 
"29.1"))
+;; Version: 1.3.0
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 ;; Created: 8th Oct 2023
 
@@ -38,10 +38,8 @@
 (require 'eieio)
 (require 'llm)
 (require 'llm-provider-utils)
-(require 'spinner)
 (require 'transient)
 (require 'compat)
-(require 'posframe)
 (eval-when-compile (require 'rx))
 
 (defgroup ellama nil
@@ -116,13 +114,22 @@ Make reasoning models more useful for many cases."
   :type '(alist :key-type string
                :value-type (sexp :validate llm-standard-provider-p)))
 
+(defvar spinner-types)
+
 (defcustom ellama-spinner-type 'progress-bar
   "Spinner type for ellama."
   :group 'ellama
-  :type `(choice ,@(mapcar
-                   (lambda (type)
-                     `(const ,(car type)))
-                   spinner-types)))
+  :type `(choice ,@(if (boundp 'spinner-types)
+                      (mapcar
+                       (lambda (type)
+                         `(const ,(car type)))
+                       spinner-types)
+                    '(const progress-bar))))
+
+(defcustom ellama-spinner-enabled nil
+  "Enable spinner during text generation."
+  :group 'ellama
+  :type 'boolean)
 
 (defcustom ellama-command-map
   (let ((map (make-sparse-keymap)))
@@ -140,7 +147,7 @@ Make reasoning models more useful for many cases."
     ;; session
     (define-key map (kbd "s l") 'ellama-load-session)
     (define-key map (kbd "s r") 'ellama-session-rename)
-    (define-key map (kbd "s d") 'ellama-session-remove)
+    (define-key map (kbd "s d") 'ellama-session-delete)
     (define-key map (kbd "s a") 'ellama-session-switch)
     ;; improve
     (define-key map (kbd "i w") 'ellama-improve-wording)
@@ -449,12 +456,6 @@ It should be a function with single argument generated 
text string."
   :group 'ellama
   :type 'symbol)
 
-(defcustom ellama-long-lines-length 100
-  "Long lines length for fill paragraph call.
-Too low value can break generated code by splitting long comment lines."
-  :group 'ellama
-  :type 'integer)
-
 (defcustom ellama-translate-italic t
   "Translate italic during markdown to org transformations."
   :group 'ellama
@@ -494,6 +495,7 @@ Too low value can break generated code by splitting long 
comment lines."
 (define-minor-mode ellama-request-mode
   "Minor mode for ellama buffers with active request to llm."
   :interactive nil
+  :lighter " ellama:generating"
   :keymap '(([remap keyboard-quit] . ellama--cancel-current-request-and-quit))
   (if ellama-request-mode
       (add-hook 'kill-buffer-hook 'ellama--cancel-current-request nil t)
@@ -522,8 +524,7 @@ Too low value can break generated code by splitting long 
comment lines."
   (if ellama-fill-paragraphs
       (with-temp-buffer
        (insert (propertize text 'hard t))
-       (let ((fill-column ellama-long-lines-length)
-             (use-hard-newlines t))
+       (let ((use-hard-newlines t))
          (fill-region (point-min) (point-max) nil t t))
        (buffer-substring-no-properties (point-min) (point-max)))
     text))
@@ -587,8 +588,7 @@ Too low value can break generated code by splitting long 
comment lines."
   ;; filling long lines
   (goto-char beg)
   (when ellama-fill-paragraphs
-    (let ((fill-column ellama-long-lines-length)
-         (use-hard-newlines t))
+    (let ((use-hard-newlines t))
       (fill-region beg end nil t t))))
 
 (defun ellama--replace-outside-of-code-blocks (text)
@@ -823,9 +823,12 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
 
 (defun ellama--cancel-current-request ()
   "Cancel current running request."
+  (declare-function spinner-stop "ext:spinner")
   (when ellama--current-request
     (llm-cancel-request ellama--current-request)
-    (spinner-stop)
+    (when ellama-spinner-enabled
+      (require 'spinner)
+      (spinner-stop))
     (setq ellama--current-request nil)))
 
 (defun ellama--cancel-current-request-and-quit ()
@@ -953,27 +956,29 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
                             `((ignore . 
(,ellama-chat-display-action-function)))))))
 
 ;;;###autoload
-(defun ellama-session-remove ()
-  "Remove ellama session."
+(defun ellama-session-delete ()
+  "Delete ellama session."
   (interactive)
   (let* ((id (completing-read
              "Select session to remove: "
              (hash-table-keys ellama--active-sessions)))
         (buffer (ellama-get-session-buffer id))
         (file (buffer-file-name buffer))
-        (session-file (ellama--get-session-file-name file))
-        (translation-file (ellama--get-translation-file-name file)))
+        (session-file (when file (ellama--get-session-file-name file)))
+        (translation-file (when file (ellama--get-translation-file-name 
file))))
     (kill-buffer buffer)
-    (delete-file file t)
-    (delete-file session-file t)
+    (when file (delete-file file t))
+    (when session-file (delete-file session-file t))
     (mapc
      (lambda (buf)
-       (when (and (buffer-file-name buf)
-                 (file-equal-p (buffer-file-name buf)
-                               translation-file))
+       (when (and
+             translation-file
+             (buffer-file-name buf)
+             (file-equal-p (buffer-file-name buf)
+                           translation-file))
         (kill-buffer buf)))
      (buffer-list))
-    (when (file-exists-p translation-file)
+    (when (and translation-file (file-exists-p translation-file))
       (delete-file translation-file t))))
 
 (defun ellama-activate-session (id)
@@ -996,39 +1001,44 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
 (defun ellama-session-rename ()
   "Rename current ellama session."
   (interactive)
-  (when-let* ((id (if ellama--current-session
-                     (ellama-session-id ellama--current-session)
-                   ellama--current-session-id))
-             (buffer (ellama-get-session-buffer id))
-             (session (with-current-buffer buffer
-                        ellama--current-session))
-             (file-name (buffer-file-name buffer))
-             (file-ext (file-name-extension file-name))
-             (dir (file-name-directory file-name))
-             (session-file-name (ellama--get-session-file-name file-name))
-             (new-id (read-string
-                      "New session name: "
-                      id))
-             (new-file-name (file-name-concat
-                             dir
-                             (concat new-id "." file-ext)))
-             (new-session-file-name
-              (ellama--get-session-file-name new-file-name)))
-    (with-current-buffer buffer
-      (set-visited-file-name new-file-name))
-    (when (file-exists-p file-name)
+  (let* ((id (if ellama--current-session
+                (ellama-session-id ellama--current-session)
+              ellama--current-session-id))
+        (buffer (when id (ellama-get-session-buffer id)))
+        (session (when buffer (with-current-buffer buffer
+                                ellama--current-session)))
+        (file-name (when buffer (buffer-file-name buffer)))
+        (file-ext (when file-name (file-name-extension file-name)))
+        (dir (when file-name (file-name-directory file-name)))
+        (session-file-name (when file-name (ellama--get-session-file-name 
file-name)))
+        (new-id (read-string
+                 "New session name: "
+                 id))
+        (new-file-name (when dir (file-name-concat
+                                  dir
+                                  (concat new-id "." file-ext))))
+        (new-session-file-name
+         (when new-file-name (ellama--get-session-file-name new-file-name))))
+    (when new-file-name (with-current-buffer buffer
+                         (set-visited-file-name new-file-name)))
+    (when buffer (with-current-buffer buffer
+                  (rename-buffer (or new-file-name new-id))))
+    (when (and file-name (file-exists-p file-name))
       (rename-file file-name new-file-name))
-    (when (file-exists-p session-file-name)
+    (when (and session-file-name (file-exists-p session-file-name))
       (rename-file session-file-name new-session-file-name))
-    (setf (ellama-session-id session) new-id)
+    (when session (setf (ellama-session-id session) new-id))
     (when (equal ellama--current-session-id id)
       (setq ellama--current-session-id new-id))
     (remhash id ellama--active-sessions)
-    (puthash new-id buffer ellama--active-sessions)))
+    (puthash new-id buffer ellama--active-sessions)
+    (when (and buffer ellama-session-auto-save)
+      (with-current-buffer buffer
+       (save-buffer)))))
 
 (defvar ellama--context-buffer " *ellama-context*")
 
-(defcustom ellama-context-posframe-enabled t
+(defcustom ellama-context-posframe-enabled nil
   "Enable showing posframe with ellama context."
   :group 'ellama
   :type 'boolean)
@@ -1040,8 +1050,7 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
   (setq ellama--global-context nil)
   (with-current-buffer ellama--context-buffer
     (erase-buffer))
-  (when ellama-context-posframe-enabled
-    (posframe-hide ellama--context-buffer)))
+  (ellama-update-context-show))
 
 ;; Context elements
 
@@ -1070,32 +1079,95 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
   :group 'ellama
   :type 'integer)
 
-(defcustom ellama-context-element-padding-size 20
-  "Padding size for context elements."
-  :group 'ellama
-  :type 'integer)
-
-(defun ellama-update-context-posframe-show ()
-  "Update and show context posframe."
+(defun ellama-update-context-show ()
+  "Update and show context in posframe of header line."
+  (declare-function posframe-show "ext:posframe")
+  (declare-function posframe-hide "ext:posframe")
+  (with-current-buffer ellama--context-buffer
+    (erase-buffer)
+    (when ellama--global-context
+      (insert (format
+              " ellama ctx: %s"
+              (string-join
+               (mapcar
+                (lambda (el)
+                  (ellama-context-element-display el))
+                ellama--global-context)
+               "  ")))))
   (when ellama-context-posframe-enabled
-    (with-current-buffer ellama--context-buffer
-      (erase-buffer)
-      (when ellama--global-context
-       (insert (format
-                "context: %s"
-                (string-join
-                 (mapcar
-                  (lambda (el)
-                    (string-pad
-                     (ellama-context-element-display el) 
ellama-context-element-padding-size))
-                  ellama--global-context)
-                 "  ")))))
+    (require 'posframe)
     (if ellama--global-context
        (posframe-show
         ellama--context-buffer
         :poshandler ellama-context-poshandler
         :internal-border-width ellama-context-border-width)
-      (posframe-hide ellama--context-buffer))))
+      (posframe-hide ellama--context-buffer)))
+  (ellama-context-update-header-line))
+
+(defface ellama-face '((t (:inherit shadow)))
+  "Base face for all ellama things.")
+
+(defface ellama-context-line-face '((t (:inherit (mode-line-buffer-id 
ellama-face))))
+  "Face for ellama context line.")
+
+(defun ellama-context-line ()
+  "Return current global context line."
+  (propertize (with-current-buffer ellama--context-buffer
+               (buffer-substring-no-properties
+                (point-min) (point-max)))
+             'help-echo "mouse-1: manage ellama context"
+             'mouse-face 'header-line-format
+             'face 'ellama-context-line-face
+             'keymap (let ((m (make-sparse-keymap)))
+                       (define-key m [header-line mouse-1] 
#'ellama-transient-context-menu)
+                       (define-key m [mode-line mouse-1] 
#'ellama-transient-context-menu)
+                       m)))
+
+;;;###autoload
+(define-minor-mode ellama-context-header-line-mode
+  "Toggle Ellama Context header line mode."
+  :group 'ellama
+  (add-hook 'window-state-change-hook #'ellama-context-update-header-line)
+  (if ellama-context-header-line-mode
+      (ellama-context-update-header-line)
+    (setq header-line-format (delete '(:eval (ellama-context-line)) 
header-line-format))))
+
+;;;###autoload
+(define-globalized-minor-mode ellama-context-header-line-global-mode
+  ellama-context-header-line-mode
+  ellama-context-header-line-mode)
+
+(defun ellama-context-update-header-line ()
+  "Update and display context information in the header line."
+  (if (and ellama-context-header-line-mode ellama--global-context)
+      (add-to-list 'header-line-format '(:eval (ellama-context-line)) t)
+    (setq header-line-format (delete '(:eval (ellama-context-line)) 
header-line-format))))
+
+;;;###autoload
+(define-minor-mode ellama-context-mode-line-mode
+  "Toggle Ellama Context mode line mode."
+  :group 'ellama
+  (add-hook 'window-state-change-hook #'ellama-context-update-mode-line)
+  (if ellama-context-mode-line-mode
+      (ellama-context-update-mode-line)
+    (setq mode-line-format (delete '(:eval (ellama-context-line)) 
mode-line-format))))
+
+;;;###autoload
+(define-globalized-minor-mode ellama-context-mode-line-global-mode
+  ellama-context-mode-line-mode
+  ellama-context-mode-line-mode)
+
+(defun ellama-context-turn-on-mode-line-mode ()
+  "Turn on `ellama-context-mode-line-mode' if appropriate."
+  (when (or (eq major-mode 'text-mode)
+            (derived-mode-p 'text-mode))
+    (ellama-context-mode-line-mode 1)))
+
+(defun ellama-context-update-mode-line ()
+  "Update and display context information in the mode line."
+  (if (and ellama-context-mode-line-mode ellama--global-context)
+      (add-to-list 'mode-line-format '(:eval (ellama-context-line)) t)
+    (setq mode-line-format (delete '(:eval (ellama-context-line)) 
mode-line-format))))
 
 (cl-defmethod ellama-context-element-add ((element ellama-context-element))
   "Add the ELEMENT to the Ellama context."
@@ -1104,7 +1176,7 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
              :test #'equal-including-properties)
   (setf ellama--global-context (nreverse ellama--global-context))
   (get-buffer-create ellama--context-buffer t)
-  (ellama-update-context-posframe-show))
+  (ellama-update-context-show))
 
 (defcustom ellama-manage-context-display-action-function 
#'display-buffer-same-window
   "Display action function for `ellama-render-context'."
@@ -1203,7 +1275,7 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
   (when-let ((elt (get-text-property (point) 'context-element)))
     (ellama-remove-context-element elt)
     (ellama-manage-context)
-    (ellama-update-context-posframe-show)))
+    (ellama-update-context-show)))
 
 ;; Buffer context element
 
@@ -1450,7 +1522,6 @@ If EPHEMERAL non nil new session will not be associated 
with any file."
   (with-temp-buffer
     (insert (propertize content 'hard t))
     (let ((fill-prefix "> ")
-         (fill-column ellama-long-lines-length)
          (use-hard-newlines t)
          (comment-start ">")
          (comment-empty-lines t))
@@ -1803,6 +1874,8 @@ failure (with BUFFER current).
 
 :on-done ON-DONE -- ON-DONE a function or list of functions that's called with
  the full response text when the request completes (with BUFFER current)."
+  (declare-function spinner-start "ext:spinner")
+  (declare-function spinner-stop "ext:spinner")
   (let* ((session-id (plist-get args :session-id))
         (session (or (plist-get args :session)
                      (when session-id
@@ -1869,7 +1942,9 @@ failure (with BUFFER current).
        (setq ellama--change-group (prepare-change-group))
        (activate-change-group ellama--change-group)
        (ellama-set-markers start end point)
-       (spinner-start ellama-spinner-type)
+       (when ellama-spinner-enabled
+         (require 'spinner)
+         (spinner-start ellama-spinner-type))
        (let ((request (llm-chat-streaming
                        provider
                        llm-prompt
@@ -1883,7 +1958,8 @@ failure (with BUFFER current).
                                      text)))
                          (with-current-buffer buffer
                            (accept-change-group ellama--change-group)
-                           (spinner-stop)
+                           (when ellama-spinner-enabled
+                             (spinner-stop))
                            (if (and (listp donecb)
                                     (functionp (car donecb)))
                                (mapc (lambda (fn) (funcall fn text))
@@ -1907,7 +1983,8 @@ failure (with BUFFER current).
                        (lambda (_ msg)
                          (with-current-buffer buffer
                            (cancel-change-group ellama--change-group)
-                           (spinner-stop)
+                           (when ellama-spinner-enabled
+                             (spinner-stop))
                            (funcall errcb msg)
                            (setq ellama--current-request nil)
                            (ellama-request-mode -1))))))
@@ -2944,7 +3021,7 @@ Call CALLBACK on result list of strings.  ARGS contains 
keys for fine control.
   [["Session Commands"
     ("l" "Load Session" ellama-load-session)
     ("r" "Rename Session" ellama-session-rename)
-    ("d" "Remove Session" ellama-session-remove)
+    ("d" "Delete Session" ellama-session-delete)
     ("a" "Activate Session" ellama-session-switch)]
    ["Quit" ("q" "Quit" transient-quit-one)]])
 
diff --git a/tests/test-ellama.el b/tests/test-ellama.el
index b88e2226ce..6b47022191 100644
--- a/tests/test-ellama.el
+++ b/tests/test-ellama.el
@@ -251,70 +251,70 @@
 ```tex
 \\documentclass{article}
 \\usepackage{tikz} \\begin{document}
-\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) 
{Text};
+\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text};
 \\end{tikzpicture}
 \\end{document}
 ```
 This code will create a rectangle with a blue border and light
-blue filling. You can replace \'Text\' with your desired text or other TikZ
-elements.")))
+blue filling. You can replace \'Text\' with your desired text
+or other TikZ elements.")))
     (should (string-equal result "Here is your TikZ code for a blue rectangle:
 #+BEGIN_SRC tex
 \\documentclass{article}
 \\usepackage{tikz} \\begin{document}
-\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) 
{Text};
+\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text};
 \\end{tikzpicture}
 \\end{document}
 #+END_SRC
 This code will create a rectangle with a blue border and light
-blue filling. You can replace \'Text\' with your desired text or other TikZ
-elements."))))
+blue filling. You can replace \'Text\' with your desired text
+or other TikZ elements."))))
 
 (ert-deftest test-ellama-md-to-org-code-hard ()
   (let ((result (ellama--translate-markdown-to-org-filter "Here is your TikZ 
code for a blue rectangle:
 ```
 \\documentclass{article}
 \\usepackage{tikz} \\begin{document}
-\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) 
{Text};
+\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text};
 \\end{tikzpicture}
 \\end{document}
 ```
 This code will create a rectangle with a blue border and light
-blue filling. You can replace \'Text\' with your desired text or other TikZ
-elements.")))
+blue filling. You can replace \'Text\' with your desired text or other
+TikZ elements.")))
     (should (string-equal result "Here is your TikZ code for a blue rectangle:
 #+BEGIN_SRC
 \\documentclass{article}
 \\usepackage{tikz} \\begin{document}
-\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) 
{Text};
+\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text};
 \\end{tikzpicture}
 \\end{document}
 #+END_SRC
 This code will create a rectangle with a blue border and light
-blue filling. You can replace \'Text\' with your desired text or other TikZ
-elements."))))
+blue filling. You can replace \'Text\' with your desired text or other
+TikZ elements."))))
 
 (ert-deftest test-ellama-md-to-org-code-nightmare ()
   (let ((result (ellama--translate-markdown-to-org-filter "Here is your TikZ 
code for a blue rectangle:
 ```
 \\documentclass{article}
 \\usepackage{tikz} \\begin{document}
-\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) 
{Text};
+\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text};
 \\end{tikzpicture}
 \\end{document}```This code will create a rectangle with a blue border and 
light
-blue filling. You can replace \'Text\' with your desired text or other TikZ
-elements.")))
+blue filling. You can replace \'Text\' with your desired text
+or other TikZ elements.")))
     (should (string-equal result "Here is your TikZ code for a blue rectangle:
 #+BEGIN_SRC
 \\documentclass{article}
 \\usepackage{tikz} \\begin{document}
-\\begin{tikzpicture} \\node[rectangle, draw=blue, fill=blue!20] (mynode) 
{Text};
+\\begin{tikzpicture} \\node[rectangle, draw=blue] (mynode) {Text};
 \\end{tikzpicture}
 \\end{document}
 #+END_SRC
 This code will create a rectangle with a blue border and light
-blue filling. You can replace \'Text\' with your desired text or other TikZ
-elements."))))
+blue filling. You can replace \'Text\' with your desired text
+or other TikZ elements."))))
 
 (ert-deftest test-ellama-md-to-org-code-multiple-bad-blocks ()
   (let ((result (ellama--translate-markdown-to-org-filter "Some text:
@@ -380,7 +380,8 @@ $P_\\theta$
     (should (string-equal result "This"))))
 
 (ert-deftest test-ellama-md-to-org-code-snake-case ()
-  (let ((result (ellama--translate-markdown-to-org-filter "```python
+  (let* ((fill-column 70)
+        (result (ellama--translate-markdown-to-org-filter "```python
 # Example of snake case variables and functions
 
 # Variable names using snake_case
@@ -407,27 +408,7 @@ In this example:
 - The function name `calculate_average_score` also follows the snake_case 
convention.
 
 Snake case helps improve readability, especially in languages that are 
sensitive to capitalization like Python.")))
-    (should (string-equal result "#+BEGIN_SRC python
-# Example of snake case variables and functions
-
-# Variable names using snake_case
-student_name = \"Alice Johnson\"
-class_name = \"Mathematics\"
-grade_level = 10
-
-# Function name using snake_case
-def calculate_average_score(math_score, science_score, english_score):
-    average_score = (math_score + science_score + english_score) / 3
-    return average_score
-
-# Using the function
-student_math_score = 85
-student_science_score = 90
-student_english_score = 78
-
-average_score = calculate_average_score(student_math_score, 
student_science_score, student_english_score)
-print(f\"The average score of {student_name} in {class_name} is: 
{average_score:.2f}\")
-#+END_SRC\n\nIn this example:\n- Variable names like ~student/name~, 
~class/name~, and ~grade/level~ use snake/case.\n- The function name 
~calculate/average/score~ also follows the snake_case convention.\n\nSnake case 
helps improve readability, especially in languages that are sensitive to 
capitalization\nlike Python."))))
+    (should (string-equal result "#+BEGIN_SRC python\n# Example of snake case 
variables and functions\n\n# Variable names using snake_case\nstudent_name = 
\"Alice Johnson\"\nclass_name = \"Mathematics\"\ngrade_level = 10\n\n# Function 
name using snake_case\ndef calculate_average_score(math_score, science_score, 
english_score):\n    average_score = (math_score + science_score + 
english_score) / 3\n    return average_score\n\n# Using the 
function\nstudent_math_score = 85\nstudent_science_s [...]
 
 (ert-deftest test-ellama--fix-file-name ()
   (should (string=


Reply via email to