summaryrefslogtreecommitdiff
path: root/kitty-graphics.el
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-02-26 10:17:11 +0100
committercashmere <cashmere@autistici.org>2026-02-26 10:17:11 +0100
commitb2398257cd020ba4ce00a44cf30c0cd362a067b6 (patch)
treefd5a9735555dbfce11a372588edd2acd540499f9 /kitty-graphics.el
parentcfd49b768a607064b9a5ec4bd37c4476777ca42b (diff)
downloadkitty-graphics-b2398257cd020ba4ce00a44cf30c0cd362a067b6.tar.gz
fix: hide images when org headings are collapsed
Add fold-aware visibility detection so images disappear when their containing heading is folded and reappear when expanded. - Add kitty-gfx--in-folded-region-p: checks org-fold (9.6+) then falls back to overlay-based invisibility, ignoring cosmetic org-link bracket hiding - Update kitty-gfx--overlay-screen-pos to skip folded overlays - Add kitty-gfx--on-org-cycle hook: deletes terminal placements before clearing cache, then schedules refresh
Diffstat (limited to 'kitty-graphics.el')
-rw-r--r--kitty-graphics.el56
1 files changed, 53 insertions, 3 deletions
diff --git a/kitty-graphics.el b/kitty-graphics.el
index 32e81d4..acc0f6f 100644
--- a/kitty-graphics.el
+++ b/kitty-graphics.el
@@ -44,6 +44,7 @@
(declare-function org-attach-dir "org-attach" (&optional create-if-not-exists-p))
(declare-function org-link-preview "org" (&optional arg beg end))
(declare-function org-link-preview-region "org" (&optional include-linked refresh beg end))
+(declare-function org-fold-folded-p "org-fold" (&optional pos spec-or-alias))
(declare-function dired-get-file-for-visit "dired" ())
(declare-function image-mode-setup-winprops "image-mode" ())
(declare-function shr-rescale-image "shr" (data &optional content-type width height max-width max-height))
@@ -245,13 +246,38 @@ Uses direct placement: move cursor, then `a=p' with `c' and `r' params."
;;;; Position mapping
+(defun kitty-gfx--in-folded-region-p (pos)
+ "Non-nil if POS is inside a folded region (collapsed heading, block, etc.).
+Checks org-fold (org 9.6+, text-property based) first, then falls
+back to overlay-based invisibility for legacy org and outline-mode.
+Ignores cosmetic invisibility like hidden link brackets (`org-link')."
+ (or
+ ;; org-fold (org 9.6+): text-property based folding.
+ ;; org-fold-folded-p only checks structural folds (outline, block,
+ ;; drawer), not cosmetic link bracket hiding.
+ (and (fboundp 'org-fold-folded-p)
+ (condition-case nil
+ (org-fold-folded-p pos)
+ (error nil)))
+ ;; Legacy / non-org overlay-based folding (outline-mode, etc.)
+ (let ((inv (get-char-property pos 'invisible)))
+ (and inv (not (eq inv 'org-link))))))
+
(defun kitty-gfx--overlay-screen-pos (ov)
"Return (TERM-ROW . TERM-COL) for overlay OV, or nil if not visible.
-Coordinates are 1-indexed terminal positions."
+Coordinates are 1-indexed terminal positions.
+Returns nil when the overlay position is inside a folded region
+\(e.g., a collapsed org heading), even if the position is within
+the window's scroll range."
(let* ((buf (overlay-buffer ov))
(pos (overlay-start ov))
(win (and buf (get-buffer-window buf))))
- (when (and win pos (pos-visible-in-window-p pos win))
+ (when (and win pos
+ (pos-visible-in-window-p pos win)
+ ;; Check that the text isn't hidden by structural folding
+ ;; (outline, org-fold, etc.) but allow cosmetic invisibility
+ ;; like org-link bracket hiding.
+ (not (kitty-gfx--in-folded-region-p pos)))
(let* ((edges (window-edges win))
(win-top (nth 1 edges))
(win-left (nth 0 edges))
@@ -593,6 +619,27 @@ BEG/END span the overlay region. MAX-COLS/MAX-ROWS limit size."
;;;; Org-mode integration
+(defun kitty-gfx--on-org-cycle (&rest _args)
+ "Handle org visibility cycling.
+Deletes all current-buffer placements from the terminal, clears the
+position cache, then schedules a refresh that re-places only the
+overlays that are still visible (not inside a fold)."
+ (when (and kitty-graphics-mode kitty-gfx--overlays)
+ (kitty-gfx--sync-begin)
+ (unwind-protect
+ (dolist (ov kitty-gfx--overlays)
+ (when (overlay-buffer ov)
+ ;; Delete this overlay's terminal placement
+ (let ((id (overlay-get ov 'kitty-gfx-id))
+ (pid (overlay-get ov 'kitty-gfx-pid)))
+ (when (and id pid)
+ (kitty-gfx--delete-placement id pid)))
+ ;; Clear position cache so visible images get re-placed
+ (overlay-put ov 'kitty-gfx-last-row nil)
+ (overlay-put ov 'kitty-gfx-last-col nil)))
+ (kitty-gfx--sync-end))
+ (kitty-gfx--schedule-refresh)))
+
(defun kitty-gfx--image-file-p (file)
"Return non-nil if FILE has an image extension."
(let ((ext (file-name-extension file)))
@@ -872,7 +919,9 @@ Registers `kitty-image' dispatcher and prepends it to the dispatcher list."
#'kitty-gfx--org-link-preview-advice))
(when (fboundp 'org-link-preview-region)
(advice-add 'org-link-preview-region :around
- #'kitty-gfx--org-link-preview-region-advice)))
+ #'kitty-gfx--org-link-preview-region-advice))
+ ;; Refresh images when org cycles heading visibility
+ (add-hook 'org-cycle-hook #'kitty-gfx--on-org-cycle))
(with-eval-after-load 'image-mode
(advice-add 'image-mode :around
#'kitty-gfx--image-mode-advice))
@@ -890,6 +939,7 @@ Registers `kitty-image' dispatcher and prepends it to the dispatcher list."
(advice-remove 'org-link-preview #'kitty-gfx--org-link-preview-advice))
(when (fboundp 'org-link-preview-region)
(advice-remove 'org-link-preview-region #'kitty-gfx--org-link-preview-region-advice))
+ (remove-hook 'org-cycle-hook #'kitty-gfx--on-org-cycle)
(advice-remove 'image-mode #'kitty-gfx--image-mode-advice)
(advice-remove 'shr-put-image #'kitty-gfx--shr-put-image-advice)
(kitty-gfx--uninstall-dirvish))