4444(require 'pi-coding-agent-table )
4545(require 'cl-lib )
4646(require 'ansi-color )
47+ (require 'image )
4748
4849; ; Forward references for functions in other modules
4950(declare-function pi-coding-agent-compact " pi-coding-agent-menu" (&optional custom-instructions))
@@ -1742,21 +1743,85 @@ streaming state changed."
17421743 event-type
17431744 event-content-index))))
17441745
1746+ (defun pi-coding-agent--image-type-from-mime (mime-type )
1747+ " Return an Emacs image type symbol for MIME-TYPE."
1748+ (pcase mime-type
1749+ (" image/png" 'png )
1750+ ((or " image/jpeg" " image/jpg" ) 'jpeg )
1751+ (" image/gif" 'gif )
1752+ (" image/webp" 'webp )
1753+ (" image/svg+xml" 'svg )))
1754+
1755+ (defun pi-coding-agent--image-block-display (block )
1756+ " Return an image display object for image content BLOCK, or nil."
1757+ (when (display-images-p )
1758+ (let* ((mime-type (plist-get block :mimeType ))
1759+ (image-type (pi-coding-agent--image-type-from-mime mime-type))
1760+ (data (plist-get block :data )))
1761+ (when (and image-type
1762+ (image-type-available-p image-type)
1763+ data)
1764+ (condition-case nil
1765+ (create-image (base64-decode-string data) image-type t
1766+ :max-width pi-coding-agent-image-preview-max-width)
1767+ (error nil ))))))
1768+
1769+ (defun pi-coding-agent--render-image-content-block (block )
1770+ " Return a display string for image content BLOCK."
1771+ (let ((fallback (format " [image%s ] "
1772+ (if-let* ((mime-type (or (plist-get block :mimeType )
1773+ (plist-get block :mime-type ))))
1774+ (format " : %s " mime-type)
1775+ " " ))))
1776+ (if-let* ((image (pi-coding-agent--image-block-display block)))
1777+ (propertize fallback
1778+ 'display image
1779+ 'rear-nonsticky t
1780+ 'help-echo fallback)
1781+ fallback)))
1782+
1783+ (defun pi-coding-agent--file-image-display (path )
1784+ " Return an image display object for local image PATH, or nil."
1785+ (when (and (stringp path)
1786+ (file-readable-p path)
1787+ (display-images-p )
1788+ (image-supported-file-p path))
1789+ (condition-case nil
1790+ (create-image path nil nil
1791+ :max-width pi-coding-agent-image-preview-max-width)
1792+ (error nil ))))
1793+
1794+ (defun pi-coding-agent--render-file-image (path )
1795+ " Return a display string for local image PATH."
1796+ (let* ((mime-type (when-let* ((image-type (image-supported-file-p path)))
1797+ (pcase image-type
1798+ ('svg " image/svg+xml" )
1799+ (_ (format " image/%s " image-type)))))
1800+ (fallback (format " [image%s ] "
1801+ (if mime-type (format " : %s " mime-type) " " ))))
1802+ (if-let* ((image (pi-coding-agent--file-image-display path)))
1803+ (propertize fallback
1804+ 'display image
1805+ 'rear-nonsticky t
1806+ 'help-echo fallback)
1807+ fallback)))
1808+
17451809(defun pi-coding-agent--extract-text-from-content (content-blocks )
1746- " Extract text from CONTENT-BLOCKS vector efficiently .
1747- Returns the concatenated text from all text blocks.
1748- Optimized for the common case of a single text block ."
1810+ " Extract displayable content from CONTENT-BLOCKS vector.
1811+ Text blocks are concatenated as text; image blocks are rendered inline when
1812+ Emacs can display them, otherwise a textual placeholder is used ."
17491813 (if (and (vectorp content-blocks) (> (length content-blocks) 0 ))
17501814 (let ((first-block (aref content-blocks 0 )))
17511815 (if (and (= (length content-blocks) 1 )
17521816 (equal (plist-get first-block :type ) " text" ))
17531817 ; ; Fast path: single text block (common case)
17541818 (or (plist-get first-block :text ) " " )
1755- ; ; Slow path: multiple blocks, need to filter and concat
1819+ ; ; Slow path: multiple blocks, render supported block types.
17561820 (mapconcat (lambda (c )
1757- (if (equal (plist-get c :type ) " text" )
1758- (or (plist-get c :text ) " " )
1759- " " ))
1821+ (pcase (plist-get c :type )
1822+ (" text" (or (plist-get c :text ) " " ))
1823+ (" image" (concat " \n " (pi-coding-agent--render-image-content-block c) " \n " ))
1824+ (_ " " )))
17601825 content-blocks " " )))
17611826 " " ))
17621827
@@ -1924,12 +1989,18 @@ if none exists, render the result at point without a live overlay."
19241989 (is-edit-diff (and (equal tool-name " edit" )
19251990 (not is-error)
19261991 (plist-get details :diff )))
1992+ (image-read-content
1993+ (when (equal tool-name " read" )
1994+ (when-let* ((path (pi-coding-agent--tool-path args))
1995+ ((pi-coding-agent--file-image-display path)))
1996+ (concat raw-output " \n " (pi-coding-agent--render-file-image path)))))
19271997 (display-content
19281998 (ansi-color-filter-apply
19291999 (pcase tool-name
19302000 (" edit" (or (plist-get details :diff ) raw-output))
19312001 (" write" (or (plist-get args :content ) raw-output))
1932- ((or " bash" " read" ) raw-output)
2002+ (" bash" raw-output)
2003+ (" read" (or image-read-content raw-output))
19332004 (_ (if-let* ((details-json
19342005 (pi-coding-agent--pretty-print-json details)))
19352006 (concat raw-output " \n\n "
@@ -1944,7 +2015,8 @@ if none exists, render the result at point without a live overlay."
19442015 (truncation (pi-coding-agent--truncate-to-visual-lines
19452016 display-content preview-limit width))
19462017 (hidden-count (plist-get truncation :hidden-lines ))
1947- (needs-collapse (> hidden-count 0 ))
2018+ (needs-collapse (and (not image-read-content)
2019+ (> hidden-count 0 )))
19482020 (inhibit-read-only t ))
19492021 (pi-coding-agent--with-scroll-preservation
19502022 (save-excursion
0 commit comments