Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
eee0f62
Initial markdown-text integration
xenodium May 17, 2026
7daeaee
Getting rid of cache to avoid regeneration. Always append.
xenodium May 17, 2026
c969958
Fixes updating fragment labels
xenodium May 18, 2026
c23c19e
Fixing inline markup rendering regression
xenodium May 19, 2026
bad3871
Bundle experimental markdown renderer
xenodium May 19, 2026
d69f0d1
Removing cl-position-if usage
xenodium May 19, 2026
b9992a3
Favor map.el
xenodium May 20, 2026
40a491d
Add padding and background color to source blocks
xenodium May 20, 2026
3a0cf87
agent-shell-markdown.el perf improvements
xenodium May 21, 2026
379d963
Improving streaming performance
xenodium May 21, 2026
25c551f
Fixes headings partially rendered when partially streamed
xenodium May 21, 2026
b5aa36f
Render blockquotes
xenodium May 21, 2026
edd88e9
Fixing blockquote test
xenodium May 21, 2026
72938b7
Strip rendered properties when pasting copied text
xenodium May 21, 2026
1a4fb1c
Improve code block handling
xenodium May 22, 2026
db3f9e2
Render code block language
xenodium May 22, 2026
eaabd86
Add copy buttons to snippets
xenodium May 22, 2026
a15c7df
Fixing code block test supporting labels + button
xenodium May 22, 2026
416e20e
Fixing incomplete table header recognition while streaming
xenodium May 23, 2026
a8eac26
Getting rid of snippet background color and all the padding
xenodium May 23, 2026
5392272
Fix body-invisible-p for whitespace-only bodies #597
xenodium May 25, 2026
19a3c22
Make code block label actual buffer text (not display only) #597
xenodium May 27, 2026
1727574
Echo "Press RET to copy" on snippet label point entering
xenodium May 28, 2026
00fcf85
Fix rendering diff #597
xenodium May 29, 2026
d2104ba
Update markdown rendering test #597
xenodium May 29, 2026
02d0722
Add markdown avoiding diff test #597
xenodium May 29, 2026
1e687e3
Fixing tests
xenodium May 29, 2026
a96176e
Make code block padded background non-trimmeable
xenodium May 29, 2026
6ac7d27
Removing surgical from function names
xenodium May 29, 2026
1b1e837
Fixing some CONTRIBUTING.org items
xenodium May 29, 2026
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
2,166 changes: 2,166 additions & 0 deletions agent-shell-markdown.el

Large diffs are not rendered by default.

457 changes: 312 additions & 145 deletions agent-shell-ui.el

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion agent-shell-viewport.el
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

(declare-function agent-shell--current-shell "agent-shell")
(declare-function agent-shell--display-buffer "agent-shell")
(declare-function agent-shell--next-command-and-response "agent-shell")
(declare-function agent-shell--get-region "agent-shell")
(declare-function agent-shell--insert-to-shell-buffer "agent-shell")
(declare-function agent-shell--make-header "agent-shell")
Expand Down Expand Up @@ -843,7 +844,7 @@ buffer from the snapshot and switch to edit mode."
(comint-next-prompt 1)
(= orig-line (point))))
(error "No next page")))
(shell-maker-next-command-and-response backwards))))
(agent-shell--next-command-and-response backwards))))
(agent-shell-viewport--initialize
:prompt (car next) :response (cdr next))
(goto-char (if start-at-top
Expand Down
140 changes: 114 additions & 26 deletions agent-shell.el
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
(require 'map)
(unless (require 'markdown-overlays nil 'noerror)
(error "Please update 'shell-maker' to v0.91.2 or newer"))
(require 'agent-shell-markdown)
(require 'agent-shell-anthropic)
(require 'agent-shell-auggie)
(require 'agent-shell-codebuddy)
Expand Down Expand Up @@ -100,6 +101,30 @@
;; lexical bindings (which would not affect `auto-insert' behavior).
(defvar auto-insert)

(defvar agent-shell--experimental-renderer nil
"When non-nil, render markdown via `agent-shell-markdown'.

Internal/experimental. `agent-shell-markdown' replaces markup
characters with propertized text in place (no overlays), which
avoids the redisplay overhead of large overlay counts but
destroys the source markdown. Defaults to nil (keep current
`markdown-overlays' behaviour).")

(defun agent-shell--render-markdown ()
"Render markdown in current (narrowed) buffer.

Dispatches to `agent-shell-markdown-replace-markup' when
`agent-shell--experimental-renderer' is non-nil; otherwise falls
back to `markdown-overlays-put'.

`markdown-overlays-*' config bindings around the call still apply
in the overlay branch; they're intentionally ignored by
`agent-shell-markdown', which always highlights blocks and renders
resolvable images."
(if agent-shell--experimental-renderer
(agent-shell-markdown-replace-markup)
(markdown-overlays-put)))

(defcustom agent-shell-permission-icon "⚠"
"Icon displayed when shell commands require permission to execute.

Expand Down Expand Up @@ -2357,6 +2382,13 @@ DIFF should be in the form returned by `agent-shell--make-diff-info':
(add-text-properties line-start line-end
'(font-lock-face diff-hunk-header))))
(forward-line 1)))
;; Tag the whole diff as already-rendered output so the
;; markdown renderer's avoid-ranges include it — context
;; lines like ` # Foo' or ` > Bar' must display verbatim,
;; not as a header / blockquote. See PR #597.
(add-text-properties (point-min) (point-max)
'(agent-shell-markdown-frozen t
rear-nonsticky (agent-shell-markdown-frozen)))
(buffer-string)))
(delete-file old-file)
(delete-file new-file))))
Expand Down Expand Up @@ -3053,7 +3085,7 @@ by default, RENDER-BODY-IMAGES to enable inline image rendering in body."
(narrow-to-region body-start body-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks)
(markdown-overlays-render-images render-body-images))
(markdown-overlays-put))))
(agent-shell--render-markdown))))
;; Note: For now, we're skipping applying markdown overlays
;; on left labels as they currently carry propertized text
;; for statuses (ie. boxed).
Expand All @@ -3065,7 +3097,7 @@ by default, RENDER-BODY-IMAGES to enable inline image rendering in body."
(narrow-to-region label-right-start label-right-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks)
(markdown-overlays-render-images nil))
(markdown-overlays-put))))
(agent-shell--render-markdown))))
(when auto-scroll
(goto-char (point-max)))))))
(with-current-buffer (map-elt state :buffer)
Expand Down Expand Up @@ -3105,26 +3137,30 @@ by default, RENDER-BODY-IMAGES to enable inline image rendering in body."
;; Marking as field to avoid false positives in
;; `agent-shell-next-item' and `agent-shell-previous-item'.
(add-text-properties (or padding-start block-start)
(or padding-end block-end) '(field output)))
;; Apply markdown overlay to body.
(when-let ((body-start (map-nested-elt range '(:body :start)))
(body-end (map-nested-elt range '(:body :end))))
(narrow-to-region body-start body-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks))
(markdown-overlays-put))
(widen))
;;
;; Note: For now, we're skipping applying markdown overlays
;; on left labels as they currently carry propertized text
;; for statuses (ie. boxed).
;;
;; Apply markdown overlay to right label.
(when-let ((label-right-start (map-nested-elt range '(:label-right :start)))
(label-right-end (map-nested-elt range '(:label-right :end))))
(narrow-to-region label-right-start label-right-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks))
(markdown-overlays-put))
(widen)))
(or padding-end block-end) '(field output))
;; Apply markdown overlay to body. `inhibit-read-only'
;; must wrap the render call too — chars in the body
;; carry `read-only t' from `agent-shell-ui--insert-fragment',
;; and `agent-shell-markdown' modifies buffer chars (unlike the
;; overlay renderer which only adds overlays).
(when-let ((body-start (map-nested-elt range '(:body :start)))
(body-end (map-nested-elt range '(:body :end))))
(narrow-to-region body-start body-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks))
(agent-shell--render-markdown))
(widen))
;;
;; Note: For now, we're skipping applying markdown overlays
;; on left labels as they currently carry propertized text
;; for statuses (ie. boxed).
;;
;; Apply markdown overlay to right label.
(when-let ((label-right-start (map-nested-elt range '(:label-right :start)))
(label-right-end (map-nested-elt range '(:label-right :end))))
(narrow-to-region label-right-start label-right-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks))
(agent-shell--render-markdown))
(widen))))
(run-hook-with-args 'agent-shell-section-functions range)))
(unless auto-scroll
(goto-char saved-point)
Expand Down Expand Up @@ -5317,13 +5353,30 @@ Returns a buffer object or nil."
(with-current-buffer shell-buffer
(goto-char comint-last-input-start))))

(defun agent-shell--command-and-response-at-point ()
"Like `shell-maker--command-and-response-at-point' but preserves
visual padding emitted by the markdown renderer inside fragments
(e.g. source-block top/bottom vpad). Delegates the raw extraction
to shell-maker and runs the result through `agent-shell-trim'."
(when-let ((cell (shell-maker--command-and-response-at-point :trimmed nil)))
(cons (agent-shell-trim (car cell))
(agent-shell-trim (cdr cell)))))

(defun agent-shell--next-command-and-response (&optional backwards)
"Like `shell-maker-next-command-and-response' but preserves
visual padding inside fragments — see
`agent-shell--command-and-response-at-point'."
(when-let ((cell (shell-maker-next-command-and-response backwards :trimmed nil)))
(cons (agent-shell-trim (car cell))
(agent-shell-trim (cdr cell)))))

(defun agent-shell-interaction-at-point ()
"Return the interaction at point in the shell buffer.
Result is of the form ((:prompt . PROMPT) (:response . RESPONSE))."
(when-let ((shell-buffer (agent-shell--shell-buffer))
(result (with-current-buffer shell-buffer
(or (shell-maker--command-and-response-at-point)
(shell-maker-next-command-and-response t)))))
(or (agent-shell--command-and-response-at-point)
(agent-shell--next-command-and-response t)))))
`((:prompt . ,(car result))
(:response . ,(cdr result)))))

Expand Down Expand Up @@ -5435,7 +5488,7 @@ inserted into the shell buffer prompt."
```" (with-current-buffer output-buffer
(buffer-string))))))
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks))
(markdown-overlays-put))
(agent-shell--render-markdown))
(when (buffer-live-p output-buffer)
(kill-buffer output-buffer)))))))
(set-process-query-on-exit-flag proc nil)
Expand Down Expand Up @@ -6173,7 +6226,7 @@ Returns an alist with insertion details or nil otherwise:
(narrow-to-region insert-start insert-end)
(let ((markdown-overlays-highlight-blocks agent-shell-highlight-blocks)
(markdown-overlays-render-images nil))
(markdown-overlays-put))))
(agent-shell--render-markdown))))
(when submit
(shell-maker-submit)))
`((:buffer . ,shell-buffer)
Expand Down Expand Up @@ -7428,6 +7481,41 @@ or select a specific request to remove."
(map-put! agent-shell--state :pending-requests nil)
(message "Removed all pending requests"))))

(defun agent-shell-trim (text)
"Strip surrounding whitespace from TEXT, preserving renderer padding.

Like `string-trim', but whitespace chars carrying the
`agent-shell-non-trimmable' text property are treated as
intentional padding (e.g. the top/bottom vpad `\\n's the
source-block renderer inserts inside a fragment body) and left
alone. A blind `string-trim' would consume those chars on the
first / last block of a response and visibly clip the panel.

For example:

(agent-shell-trim \"\\n\\n hello \\n\\n\")
=> \"hello\"

(agent-shell-trim
(concat \"\\n\\nhello\\n\"
(propertize \"\\n\" \\='agent-shell-non-trimmable t)
\"\\n\\n\"))
=> \"hello\\n\\n\" ;; tagged trailing `\\n' preserved"
(and-let* ((text text)
(start 0)
(end (length text)))
(while (and (< start end)
(memq (seq-elt text start) '(?\s ?\t ?\n ?\r))
(not (get-text-property
start 'agent-shell-non-trimmable text)))
(setq start (1+ start)))
(while (and (< start end)
(memq (seq-elt text (1- end)) '(?\s ?\t ?\n ?\r))
(not (get-text-property
(1- end) 'agent-shell-non-trimmable text)))
(setq end (1- end)))
(substring text start end)))

(provide 'agent-shell)

;;; agent-shell.el ends here
Loading