Skip to content

Add transient menu #343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
109 changes: 109 additions & 0 deletions chatgpt-shell-transient.el
Original file line number Diff line number Diff line change
@@ -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
5 changes: 4 additions & 1 deletion chatgpt-shell.el
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."
Expand Down Expand Up @@ -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)
Expand Down