macos: clipboard image paste via cmd+v (pngpaste)

my/smart-paste bound to s-v (cmd+v) in org-mode and markdown-mode:
- clipboard has image → my/paste-image-from-clipboard
- clipboard has text  → normal yank

my/paste-image-from-clipboard:
- saves to attachments/ relative to current file
- filename: image-YYYYMMDD-HHMMSS.png
- org-mode:      [[./attachments/image-....png]]
- markdown-mode: ![filename](./attachments/image-....png)
- creates attachments/ dir if missing

Requires: brew install pngpaste
This commit is contained in:
2026-02-23 14:07:21 +01:00
parent 9d8603d735
commit 312b6f0eab

View File

@@ -84,6 +84,64 @@
(after! evil
(setq evil-want-clipboard t))
;;; ============================================================
;;; CLIPBOARD IMAGE PASTE (macOS — cmd+v)
;;; ============================================================
;; Requires: brew install pngpaste
;;
;; cmd+v behaviour:
;; clipboard has image → saves to attachments/ → inserts link
;; clipboard has text → normal paste (yank)
;;
;; Org: [[./attachments/image-TIMESTAMP.png]]
;; Markdown: ![image-TIMESTAMP.png](./attachments/image-TIMESTAMP.png)
;;
;; attachments/ is always relative to the current file's directory.
(defun my/clipboard-has-image-p ()
"Return non-nil if macOS clipboard contains an image (requires pngpaste)."
(and (executable-find "pngpaste")
(let ((tmp (make-temp-file "emacs-imgcheck" nil ".png")))
(prog1 (= 0 (call-process "pngpaste" nil nil nil tmp))
(ignore-errors (delete-file tmp))))))
(defun my/paste-image-from-clipboard ()
"Save clipboard image to attachments/ subdir and insert link at point.
File: image-YYYYMMDD-HHMMSS.png. Supports org-mode and markdown-mode."
(interactive)
(let* ((base-dir (if buffer-file-name
(file-name-directory (buffer-file-name))
default-directory))
(attach-dir (expand-file-name "attachments" base-dir))
(filename (format-time-string "image-%Y%m%d-%H%M%S.png"))
(filepath (expand-file-name filename attach-dir))
(relpath (concat "attachments/" filename)))
(make-directory attach-dir t)
(if (= 0 (call-process "pngpaste" nil nil nil filepath))
(progn
(insert (pcase major-mode
('org-mode (format "[[./%s]]" relpath))
('markdown-mode (format "![%s](./%s)" filename relpath))
(_ relpath)))
(message "✓ Image saved: %s" relpath))
(error "pngpaste failed — no image in clipboard?"))))
(defun my/smart-paste ()
"Paste image if clipboard has one, else normal yank (text paste).
Bound to cmd+v in org-mode and markdown-mode."
(interactive)
(if (my/clipboard-has-image-p)
(my/paste-image-from-clipboard)
(yank)))
;; Bind cmd+v to smart paste in org and markdown
(map! :after org
:map org-mode-map
"s-v" #'my/smart-paste)
(map! :after markdown-mode
:map markdown-mode-map
"s-v" #'my/smart-paste)
;; macOS Zoom accessibility — cancel persp-mode's 2.5s cache timer after startup
;; (reduces unnecessary redraws that cause Zoom to jump)
(run-with-timer 3 nil