;; Watch https://asciinema.org/a/631451 for a demo (defconst my/termux-p (getenv "ANDROID_ROOT")) (defun my/clipboard-get () "Return the text on the system clipboard. This function treats Termux systems differently, the clipboard is only accessible through the termux-clipboard-get commandline interface, part of the the termux-api package." (if my/termux-p (shell-command-to-string "termux-clipboard-get") (current-kill 0))) (defun my/get-url-title (url) "Attempt to retrieve the title string from the given URL. Assuming the URL points to an HTML source. Returns nil if there is a non-200 return status or no title could be found." (let* ((command (format "curl --fail --silent %s" url)) (html (shell-command-to-string command)) (regexp (rx (seq "" (group (+ (not (any "<" ">")))) ""))) (matches (string-match regexp html))) (match-string 1 html))) (defun my/denote/url-clipboard () "Return the URL from the system clipboard, if any." (let ((clipboard (my/clipboard-get))) (when (org-url-p clipboard) clipboard))) (defvar my/denote/url-functions '(thing-at-point-url-at-point my/denote/url-clipboard) "List of function symbols to call to get an URL candidate. Each function should return a string with the URL or a cons cell (URL . TITLE), where title is either a string or a function returning a string.") (defun my/denote/url (url &optional title) "Create a new Org-based note based on a URL. URL can be a string or a cons cell (URL . TITLE). The TITLE, in turn, can be a string or a function (without arguments) to retrieve the title. When called interactively, the candidate URLs are obtained from the variable `my/denote/url-functions' (e.g. takes the URL from the clipboard). In case no TTTLE is passed to this function, or the URL wasn't paired with a title value, the title is obtained by curl(1) by looking at the tags." (interactive (list (let* ((prompt (format-prompt "URL" "")) (candidate-urls (-non-nil (mapcar #'funcall my/denote/url-functions))) (url (if (eql 1 (length candidate-urls)) (read-string prompt (caar candidate-urls)) (completing-read prompt candidate-urls)))) ;; `candidate-urls' is a mix of strings and cons ;; cells. If the selected URL comes from a cons ;; cell, (assoc) will return it. If it comes from a ;; string valuo, (assoc) will return nil. In that ;; case return the URL as is. (or (assoc url candidate-urls #'string=) url)) nil)) (denote (read-string (format-prompt "Title" "") ;; initial input. If no title was passed, see if it ;; can be obtained from the URL value (the cdr if the ;; url was a cons cell. (or title (cond ((and (consp url) (stringp (cdr url))) (cdr url)) ((and (consp url) (functionp (cdr url))) (funcall (cdr url))) ((stringp url) (my/get-url-title url))))) (denote-keywords-prompt) 'org (denote-subdirectory-prompt)) (org-set-property "URL" (if (consp url) (car url) url)) (goto-char (point-max)) ;; Requires kagi.el at https://codeberg.org/bram85/kagi.el (require 'kagi) (when (yes-or-no-p "Insert summary?") (kagi-summarize-url url t (completing-read (format-prompt "To language" "EN") '("EN" "NL"))))) ;;; Elfeed integration (defun my/elfeed/entry-url () "Return the URL of the current elfeed entry." (when-let ((entry (or elfeed-show-entry (elfeed-search-selected :ignore-region)))) (cons (elfeed-entry-link entry) (elfeed-entry-title entry)))) (add-to-list 'my/denote/url-functions #'my/elfeed/entry-url)