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))
@@ -1597,21 +1598,85 @@ streaming state changed."
15971598 event-type
15981599 event-content-index))))
15991600
1601+ (defun pi-coding-agent--image-type-from-mime (mime-type )
1602+ " Return an Emacs image type symbol for MIME-TYPE."
1603+ (pcase mime-type
1604+ (" image/png" 'png )
1605+ ((or " image/jpeg" " image/jpg" ) 'jpeg )
1606+ (" image/gif" 'gif )
1607+ (" image/webp" 'webp )
1608+ (" image/svg+xml" 'svg )))
1609+
1610+ (defun pi-coding-agent--image-block-display (block )
1611+ " Return an image display object for image content BLOCK, or nil."
1612+ (when (display-images-p )
1613+ (let* ((mime-type (plist-get block :mimeType ))
1614+ (image-type (pi-coding-agent--image-type-from-mime mime-type))
1615+ (data (plist-get block :data )))
1616+ (when (and image-type
1617+ (image-type-available-p image-type)
1618+ data)
1619+ (condition-case nil
1620+ (create-image (base64-decode-string data) image-type t
1621+ :max-width pi-coding-agent-image-preview-max-width)
1622+ (error nil ))))))
1623+
1624+ (defun pi-coding-agent--render-image-content-block (block )
1625+ " Return a display string for image content BLOCK."
1626+ (let ((fallback (format " [image%s ] "
1627+ (if-let* ((mime-type (or (plist-get block :mimeType )
1628+ (plist-get block :mime-type ))))
1629+ (format " : %s " mime-type)
1630+ " " ))))
1631+ (if-let* ((image (pi-coding-agent--image-block-display block)))
1632+ (propertize fallback
1633+ 'display image
1634+ 'rear-nonsticky t
1635+ 'help-echo fallback)
1636+ fallback)))
1637+
1638+ (defun pi-coding-agent--file-image-display (path )
1639+ " Return an image display object for local image PATH, or nil."
1640+ (when (and (stringp path)
1641+ (file-readable-p path)
1642+ (display-images-p )
1643+ (image-supported-file-p path))
1644+ (condition-case nil
1645+ (create-image path nil nil
1646+ :max-width pi-coding-agent-image-preview-max-width)
1647+ (error nil ))))
1648+
1649+ (defun pi-coding-agent--render-file-image (path )
1650+ " Return a display string for local image PATH."
1651+ (let* ((mime-type (when-let* ((image-type (image-supported-file-p path)))
1652+ (pcase image-type
1653+ ('svg " image/svg+xml" )
1654+ (_ (format " image/%s " image-type)))))
1655+ (fallback (format " [image%s ] "
1656+ (if mime-type (format " : %s " mime-type) " " ))))
1657+ (if-let* ((image (pi-coding-agent--file-image-display path)))
1658+ (propertize fallback
1659+ 'display image
1660+ 'rear-nonsticky t
1661+ 'help-echo fallback)
1662+ fallback)))
1663+
16001664(defun pi-coding-agent--extract-text-from-content (content-blocks )
1601- " Extract text from CONTENT-BLOCKS vector efficiently .
1602- Returns the concatenated text from all text blocks.
1603- Optimized for the common case of a single text block ."
1665+ " Extract displayable content from CONTENT-BLOCKS vector.
1666+ Text blocks are concatenated as text; image blocks are rendered inline when
1667+ Emacs can display them, otherwise a textual placeholder is used ."
16041668 (if (and (vectorp content-blocks) (> (length content-blocks) 0 ))
16051669 (let ((first-block (aref content-blocks 0 )))
16061670 (if (and (= (length content-blocks) 1 )
16071671 (equal (plist-get first-block :type ) " text" ))
16081672 ; ; Fast path: single text block (common case)
16091673 (or (plist-get first-block :text ) " " )
1610- ; ; Slow path: multiple blocks, need to filter and concat
1674+ ; ; Slow path: multiple blocks, render supported block types.
16111675 (mapconcat (lambda (c )
1612- (if (equal (plist-get c :type ) " text" )
1613- (or (plist-get c :text ) " " )
1614- " " ))
1676+ (pcase (plist-get c :type )
1677+ (" text" (or (plist-get c :text ) " " ))
1678+ (" image" (concat " \n " (pi-coding-agent--render-image-content-block c) " \n " ))
1679+ (_ " " )))
16151680 content-blocks " " )))
16161681 " " ))
16171682
@@ -1779,12 +1844,18 @@ if none exists, render the result at point without a live overlay."
17791844 (is-edit-diff (and (equal tool-name " edit" )
17801845 (not is-error)
17811846 (plist-get details :diff )))
1847+ (image-read-content
1848+ (when (equal tool-name " read" )
1849+ (when-let* ((path (pi-coding-agent--tool-path args))
1850+ ((pi-coding-agent--file-image-display path)))
1851+ (concat raw-output " \n " (pi-coding-agent--render-file-image path)))))
17821852 (display-content
17831853 (ansi-color-filter-apply
17841854 (pcase tool-name
17851855 (" edit" (or (plist-get details :diff ) raw-output))
17861856 (" write" (or (plist-get args :content ) raw-output))
1787- ((or " bash" " read" ) raw-output)
1857+ (" bash" raw-output)
1858+ (" read" (or image-read-content raw-output))
17881859 (_ (if-let* ((details-json
17891860 (pi-coding-agent--pretty-print-json details)))
17901861 (concat raw-output " \n\n "
@@ -1799,7 +1870,8 @@ if none exists, render the result at point without a live overlay."
17991870 (truncation (pi-coding-agent--truncate-to-visual-lines
18001871 display-content preview-limit width))
18011872 (hidden-count (plist-get truncation :hidden-lines ))
1802- (needs-collapse (> hidden-count 0 ))
1873+ (needs-collapse (and (not image-read-content)
1874+ (> hidden-count 0 )))
18031875 (inhibit-read-only t ))
18041876 (pi-coding-agent--with-scroll-preservation
18051877 (save-excursion
0 commit comments