Org-protocol command

(define-command-global org-capture ()
  (let* ((url (quri:url-encode (buffer-url)))
         (title (quri:url-encode (buffer-title)))
         (body (quri:url-encode (%copy)))
         (org-protocol-uri
          (format nil
           "'org-protocol://capture?template=w&url=~a&title=~a&body=~a'"
           url title body)))
    (format *error-output* "Sending to Emacs:~%~a~%" org-protocol-uri)
    (uiop:run-program
     (list "timeout" "--signal=9" "5m" "emacsclient"
           org-protocol-uri))))

;; For most users, (list "emacsclient" org-protocol-uri) may be adequate.

;; Helper functions

(defun buffer-url ()
  "Returns the URL of the current buffer."
  (quri:render-uri (url (current-buffer))))

(defun buffer-title ()
  "Returns the title of the current buffer."
  (title (current-buffer)))

3 Likes

I had a similar idea myself the other day. I actually wrote something very similar if only I had known earlier you had done all the dirty work haha. My version is pretty similar but offers storing links as well as opening org-capture. As well as a nice prompt interface in case you wanna access more than one template.

(defun get-org-templates ()
  (read-from-string (uiop:run-program
                     (list "timeout" "--signal=9" "5m" "emacsclient" "--eval"
                           "org-capture-templates")
                     :output :string)))

(defun template-description (template)
  (make-instance 'prompter:suggestion
                 :value (first template)
                 :attributes `(("Key" ,(first template))
                               ("Description" ,(second template))
                               ("Template" ,(car (last template))))))


(defun make-org-template-source ()
  "Create a prompt source based on the currently running emacs's
org-templates"
  (make-instance 'prompter:source
                 :name "Emacs Capture Templates"
                 :constructor (mapcar #'template-description
                                      ;; Ensure that we are not listing prefix keys
                                      ;; e.g. m in the me
                                      (remove-if-not #'cddr (get-org-templates)))))

(define-command-global org-capture (&optional (buffer (nyxt:current-buffer)))
  "Call org-protocol whith the type of capture using the BUFFER for
context."
  (org-protocol "capture" buffer))

(define-command-global org-protocol
    (&optional (protocol "store-link") (buffer (nyxt:current-buffer)))
  "Using the supported org-protocol type PROTOCOL execute it against the
given BUFFER's current url."
  (let ((url (render-url (nyxt:url buffer)))
        (title (title buffer))
        (body (quri:url-encode (%copy)))
        (protocol-str (format nil "org-protocol://~a?" protocol))
        (capture-template (when (equal protocol "capture")
                            (format nil "template=~a&"
                                    (first (prompt :prompt "Select a capture template"
                                                   :sources (list (make-org-template-source))))))))
    (uiop:launch-program (list "emacsclient"
                               ;; nyxt:*open-program* would also be an alternative
                               (str:concat
                                protocol-str
                                capture-template
                                (format nil "url=~a&title=~a&body=~a"
                                        url title body))))))

Stole the idea of using timeout which I had never even thought of :sweat_smile:

It’s actually a little better than this gif shows since it removes the blank templates now.
org-protocol

2 Likes

Hi.

I am trying to get org-capture working with Nyxt. USing your code I am getting an error that NYXT-USER::%COPY is undefined. Is this code working for you?

Sorry, I stopped using Nyxt years ago.

OK. Thanks for the reply. Appreciate it. Maybe I can take your code and run with it if that's OK.

Of course. Good luck.