diff --git a/README.org b/README.org index 5992369..4af1ade 100644 --- a/README.org +++ b/README.org @@ -76,6 +76,60 @@ I'm a big fan of quickly disposing of Emacs buffers with the =q= binding. chatgp LLM being lazy and returning partial code? Press =e= to request entire snippet. +*** Quick Actions with Transient Menu + + For quick access to common actions, =chatgpt-shell= provides a transient menu, + powered by the excellent [[https://github.com/magit/transient][transient]] package. Think of it as a temporary keymap + overlay that pops up, shows you available commands with their keybindings, + and disappears after you select one. + + Invoke it with =C-c C-t= while inside a =chatgpt-shell= buffer. + + #+BEGIN_SRC text :exports code + ┌──────────────────────────────────────────────────────────────────────────────┐ + │ ChatGPT Shell actions Shell Context Actions │ + │ Core Shell & Compose Shell Buffer Management │ + │ s: Focus/Start Shell C: Clear Shell Buffer │ + │ N: Start New Shell I: Interrupt Request │ + │ e: Compose Prompt Shell Navigation │ + │ p: Prompt (minibuffer) h: Search History │ + │ P: Prompt (append kill) j: Next Item │ + │ q: Quick Insert k: Previous Item │ + │ Region Actions J: Next Source Block │ + │ r: Send Region K: Previous Source Block │ + │ R: Send & Review Region Block Actions │ + │ d: Describe Code x: Execute Block │ + │ f: Refactor Code E: Edit Block │ + │ g: Write Git Commit V: View Block │ + │ t: Generate Unit Test Shell Configuration │ + │ w: Proofread Region m: Swap Model │ + │ y: Swap System Prompt │ + │ Shell Session │ + │ S: Save Transcript │ + │ O: Restore Transcript │ + │ Configuration & Utilities │ + │ Model & Configuration │ + │ L: Reload Default Models │ + │ Other │ + │ i: Describe Image │ + │ v: Show Version │ + └──────────────────────────────────────────────────────────────────────────────┘ + #+END_SRC + + (Note: The exact commands shown depend on context, like whether a region is active + or if you are inside a =chatgpt-shell= buffer.) + + If you prefer a global keybinding (available everywhere in Emacs), you can add + something like this to your personal Emacs configuration: + + #+begin_src emacs-lisp :lexical no + ;; In your init.el or equivalent + (global-set-key (kbd "C-c C-g") 'chatgpt-shell-transient) + #+end_src + + This menu helps with discovering available commands and executing them quickly + without needing to remember every single keybinding or =M-x= command name. + ** Confirm inline mods (via diffs) Request inline modifications, with explicit confirmation before accepting. diff --git a/chatgpt-shell-transient.el b/chatgpt-shell-transient.el new file mode 100644 index 0000000..9ed1df6 --- /dev/null +++ b/chatgpt-shell-transient.el @@ -0,0 +1,109 @@ +;;; chatgpt-shell-transient.el --- Transient menus for chatgpt-shell -*- lexical-binding: t; -*- + +;;; Commentary: +;; Provides transient menus for interacting with chatgpt-shell. + +;;; Code: + +(require 'transient) + +(defun chatgpt-shell-transient--in-shell-p () + "Return non-nil if the current buffer is in chatgpt-shell-mode." + (derived-mode-p 'chatgpt-shell-mode)) + +;;;###autoload +(transient-define-prefix chatgpt-shell-transient--popup () + "Transient menu for chatgpt-shell commands." + :transient-suffix 'chatgpt-shell-transient--popup--suffix + [ ;; Group 1: Always available core actions + "ChatGPT Shell actions" + ["Core Shell & Compose" + ("s" "Focus/Start Shell" chatgpt-shell) + ("N" "Start New Shell" (lambda () (interactive) (chatgpt-shell t))) + ("e" "Compose Prompt" chatgpt-shell-prompt-compose) + ("p" "Prompt (minibuffer)" + (lambda () + (interactive) + (transient-quit-one) + (run-with-idle-timer 0 nil #'chatgpt-shell-prompt))) + ("P" "Prompt (append kill)" + (lambda () + (interactive) + (transient-quit-one) + (run-with-idle-timer 0 nil #'chatgpt-shell-prompt-appending-kill-ring))) + ("q" "Quick Insert" + (lambda () + (interactive) + (transient-quit-one) + (run-with-idle-timer 0 nil #'chatgpt-shell-quick-insert)))] + + ;; Group 2: Region-specific actions (available if region is active) + ["Region Actions" + ("r" "Send Region" (lambda () (interactive) (chatgpt-shell-send-region nil)) :if region-active-p) + ("R" "Send & Review Region" chatgpt-shell-send-and-review-region :if region-active-p) + ("d" "Describe Code" chatgpt-shell-describe-code :if region-active-p) + ("f" "Refactor Code" chatgpt-shell-refactor-code :if region-active-p) + ("g" "Write Git Commit" chatgpt-shell-write-git-commit :if region-active-p) + ("t" "Generate Unit Test" chatgpt-shell-generate-unit-test :if region-active-p) + ("w" "Proofread Region" chatgpt-shell-proofread-region :if region-active-p)] + ] + + [ ;; Group 3: Shell-specific actions (available only in chatgpt-shell buffer) + "Shell Context Actions" + ["Shell Buffer Management" + ("C" "Clear Shell Buffer" chatgpt-shell-clear-buffer :if chatgpt-shell-transient--in-shell-p) + ("I" "Interrupt Request" chatgpt-shell-interrupt :if chatgpt-shell-transient--in-shell-p)] + + ["Shell Navigation" + ("h" "Search History" + (lambda () + (interactive) + (transient-quit-one) + (run-with-idle-timer 0 nil #'chatgpt-shell-search-history)) :if chatgpt-shell-transient--in-shell-p) + ("j" "Next Item" chatgpt-shell-next-item :if chatgpt-shell-transient--in-shell-p) + ("k" "Previous Item" chatgpt-shell-previous-item :if chatgpt-shell-transient--in-shell-p) + ("J" "Next Source Block" chatgpt-shell-next-source-block :if chatgpt-shell-transient--in-shell-p) + ("K" "Previous Source Block" chatgpt-shell-previous-source-block :if chatgpt-shell-transient--in-shell-p)] + + ["Block Actions" + ("x" "Execute Block" chatgpt-shell-execute-babel-block-action-at-point :if chatgpt-shell-transient--in-shell-p) + ("E" "Edit Block" chatgpt-shell-edit-block-at-point :if chatgpt-shell-transient--in-shell-p) + ("V" "View Block" chatgpt-shell-view-block-at-point :if chatgpt-shell-transient--in-shell-p)] + + ["Shell Configuration" + ("m" "Swap Model" chatgpt-shell-swap-model :if chatgpt-shell-transient--in-shell-p) + ("y" "Swap System Prompt" chatgpt-shell-swap-system-prompt :if chatgpt-shell-transient--in-shell-p)] + + ["Shell Session" + ("S" "Save Transcript" + (lambda () + (interactive) + (transient-quit-one) + (run-with-idle-timer 0 nil #'chatgpt-shell-save-session-transcript)) :if chatgpt-shell-transient--in-shell-p) + ("O" "Restore Transcript" chatgpt-shell-restore-session-from-transcript :if chatgpt-shell-transient--in-shell-p)] + ] + + [ ;; Group 4: General utilities (mostly always available) + "Configuration & Utilities" + ["Model & Configuration" + ("L" "Reload Default Models" chatgpt-shell-reload-default-models)] + + ["Other" + ("v" "Show Version" chatgpt-shell-version)] + ] + ) + +(transient-define-suffix chatgpt-shell-transient--popup--suffix () + :description "ChatGPT Shell Transient Suffix" + :class 'transient-suffix) + + +;;;###autoload +(defun chatgpt-shell-transient () + "Invoke the main transient menu for chatgpt-shell." + (interactive) + (chatgpt-shell-transient--popup)) + +(provide 'chatgpt-shell-transient) + +;;; chatgpt-shell-transient.el ends here diff --git a/chatgpt-shell.el b/chatgpt-shell.el index 529bf98..b28244c 100644 --- a/chatgpt-shell.el +++ b/chatgpt-shell.el @@ -5,7 +5,7 @@ ;; Author: Alvaro Ramirez https://xenodium.com ;; URL: https://github.com/xenodium/chatgpt-shell ;; Version: 2.16.4 -;; Package-Requires: ((emacs "28.1") (shell-maker "0.76.3")) +;; Package-Requires: ((emacs "28.1") (shell-maker "0.76.3") (transient "0.4.0")) (defconst chatgpt-shell--version "2.16.4") ;; This package is free software; you can redistribute it and/or modify @@ -84,6 +84,7 @@ (require 'chatgpt-shell-openrouter) (require 'chatgpt-shell-perplexity) (require 'chatgpt-shell-prompt-compose) +(require 'chatgpt-shell-transient) (defcustom chatgpt-shell-request-timeout 600 "How long to wait for a request to time out in seconds." @@ -822,6 +823,8 @@ Set SYSTEM-PROMPT to override variable `chatgpt-shell-system-prompt'" #'chatgpt-shell-next-item) (define-key chatgpt-shell-mode-map (kbd "C-c C-e") #'chatgpt-shell-prompt-compose) + (define-key chatgpt-shell-mode-map (kbd "C-c C-t") + #'chatgpt-shell-transient) shell-buffer)) (defun chatgpt-shell--shrink-system-prompt (prompt)