aboutsummaryrefslogtreecommitdiff
path: root/modules/org-capture-config.el
diff options
context:
space:
mode:
Diffstat (limited to 'modules/org-capture-config.el')
-rw-r--r--modules/org-capture-config.el63
1 files changed, 56 insertions, 7 deletions
diff --git a/modules/org-capture-config.el b/modules/org-capture-config.el
index 292e26a7..b7250f1e 100644
--- a/modules/org-capture-config.el
+++ b/modules/org-capture-config.el
@@ -157,6 +157,8 @@ re-scanning large target files after the first successful lookup."
(interactive)
(user-error "Key disabled during capture -- finalize with C-c C-c or abort with C-c C-k"))
+(defvar org-capture-mode-map)
+
(with-eval-after-load 'org-capture
(dolist (key '("<f1>" "<f10>" "<f11>" "<f12>" "M-SPC"))
(keymap-set org-capture-mode-map key #'cj/--org-capture-blocked-key)))
@@ -381,10 +383,10 @@ A popup still mid-capture has capture UI and is not reapable, so it is spared."
(defun cj/org-capture-reap-popup-frames ()
"Delete every quick-capture popup frame that no longer shows capture UI.
Reaps across ALL frames, not just the selected one: a capture that finalizes,
-aborts, or errors while the daemon's selected frame is something else (the common
-multi-frame case) still cleans up its \"org-capture\" popup, while a popup
-mid-capture is spared. Never deletes the last remaining frame. Safe to call
-anytime — bound to nothing, run via M-x when a stray popup needs clearing."
+aborts, or errors while the daemon's selected frame is something else (the
+common multi-frame case) still cleans up its \"org-capture\" popup, while a
+popup mid-capture is spared. Never deletes the last remaining frame. Safe to
+call anytime — bound to nothing, run via M-x when a stray popup needs clearing."
(interactive)
(dolist (f (frame-list))
(when (and (frame-live-p f)
@@ -395,9 +397,7 @@ anytime — bound to nothing, run via M-x when a stray popup needs clearing."
(window-list f 'no-minibuf))))
(delete-frame f))))
-;; Reap on every capture exit. `remove-hook' first so a live module reload swaps
-;; the retired narrow (selected-frame) handler for this one without leaving both.
-(remove-hook 'org-capture-after-finalize-hook #'cj/org-capture--delete-popup-frame)
+;; Reap on every capture exit.
(add-hook 'org-capture-after-finalize-hook #'cj/org-capture-reap-popup-frames)
;; The popup opens a fresh emacsclient frame still showing the daemon's last
@@ -437,6 +437,55 @@ never split the small floating frame."
'(cj/org-capture--popup-display-condition
cj/org-capture--display-sole-window))
+;; A fresh "org-capture" popup opens showing the daemon's last buffer (see the
+;; comment above), and only the capture UI + the reap-on-finalize hook clear it.
+;; If a capture aborts before its UI paints (a C-g, an erroring template, a path
+;; that skips `cj/quick-capture'), the popup lingers showing whatever was current
+;; -- and if that was a live terminal (an eat/vterm Claude Code buffer), eat
+;; sizes the terminal to that small popup window and clamps the real frame down
+;; to the popup's rows. These two guards keep the popup from ever holding a
+;; size-sensitive live buffer: it only ever shows capture UI or *scratch*.
+
+(defun cj/org-capture--neutralize-frame (frame)
+ "Point every non-capture-UI window of the \"org-capture\" popup FRAME at
+*scratch*. Capture UI (the *Org Select* menu, a CAPTURE-* buffer) is spared, so
+this never disturbs a live capture; it only evicts a stray live buffer (the
+daemon's last buffer on open, or a buffer restored on abort) that would
+otherwise mirror the popup's size onto its source buffer. Idempotent: a window
+already on *scratch* is left alone, so it can't loop through the
+`window-buffer-change-functions' it fires."
+ (when (and (frame-live-p frame)
+ (equal (frame-parameter frame 'name) "org-capture"))
+ (dolist (w (window-list frame 'no-minibuf))
+ (let ((name (buffer-name (window-buffer w))))
+ (unless (or (cj/org-capture--popup-sole-window-p "org-capture" name)
+ (equal name "*scratch*"))
+ (set-window-buffer w (get-buffer-create "*scratch*")))))))
+
+;; Guard 1 (root cause): neutralize the popup the instant it is created, before
+;; any capture UI paints, so it never opens mirroring the daemon's last buffer.
+(defun cj/org-capture--neutralize-new-frame (frame)
+ "Neutralize a freshly-made \"org-capture\" popup FRAME on creation.
+See `cj/org-capture--neutralize-frame'."
+ (cj/org-capture--neutralize-frame frame))
+
+(add-hook 'after-make-frame-functions #'cj/org-capture--neutralize-new-frame)
+
+;; Guard 2 (safety net): catch any path the finalize reap misses. If a live
+;; buffer is displayed in the popup after creation (an aborted capture restoring
+;; the previous buffer, a stray `switch-to-buffer'), evict it at once.
+(defun cj/org-capture--neutralize-on-buffer-change (frame-or-window)
+ "Neutralize the \"org-capture\" popup after any buffer change in FRAME-OR-WINDOW.
+`window-buffer-change-functions' passes a frame (global hook) or a window
+(buffer-local); handle both."
+ (let ((frame (if (windowp frame-or-window)
+ (window-frame frame-or-window)
+ frame-or-window)))
+ (cj/org-capture--neutralize-frame frame)))
+
+(add-hook 'window-buffer-change-functions
+ #'cj/org-capture--neutralize-on-buffer-change)
+
;; The desktop quick-capture popup is launched globally (no browser selection,
;; no mu4e message, no pdf/epub buffer), so the context-dependent templates make
;; no sense there. `cj/quick-capture' captures a single Task straight into the