jao-custom-eww.el 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. ;; -*- lexical-binding: t -*-
  2. ;;; integration with browse-url and afio
  3. (defun jao-eww-browse-url (url &rest _r)
  4. "Browse URL using eww."
  5. (if (derived-mode-p 'eww-mode)
  6. (eww url)
  7. (jao-afio-goto-www)
  8. (select-window (frame-first-window))
  9. (let* ((url (url-encode-url url))
  10. (bf (seq-find `(lambda (b)
  11. (with-current-buffer b
  12. (string= ,url
  13. (url-encode-url (eww-current-url)))))
  14. (jao-eww-session-eww-buffers))))
  15. (cond (bf (switch-to-buffer bf))
  16. ((string-match-p url "^file://") (eww-open-file url))
  17. (t (eww url 4))))))
  18. (setq jao-browse-url-function #'jao-eww-browse-url)
  19. (setq eww-use-browse-url "^\\(gemini\\|gopher\\):")
  20. ;;; multipart html renderer
  21. (defun jao-shr-html-renderer (handle)
  22. (let ((fill-column nil)
  23. (shr-width 150)
  24. (shr-max-width 150))
  25. (mm-shr handle)))
  26. (setq mm-text-html-renderer #'jao-shr-html-renderer)
  27. ;;; opening URLs
  28. (defun jao-eww-copy-link ()
  29. (interactive)
  30. (when-let (lnk (or (car (eww-links-at-point)) (eww-current-url)))
  31. (message "%s" lnk)
  32. (kill-new lnk)))
  33. (defun jao-eww-browse (arg)
  34. (interactive "P" eww-mode)
  35. (setq eww-prompt-history
  36. (cl-remove-duplicates eww-prompt-history :test #'string=))
  37. (let ((url (completing-read (if arg "eww in new buffer: " "eww: ")
  38. eww-prompt-history nil nil nil
  39. 'eww-prompt-history (eww-current-url))))
  40. (eww url (when arg 4))))
  41. (defun jao-eww-browse-new ()
  42. (interactive nil eww-mode)
  43. (jao-eww-browse t))
  44. (defun jao-eww-reload (images)
  45. (interactive "P" eww-mode)
  46. (if images
  47. (let ((shr-blocked-images nil))
  48. (eww-reload t))
  49. (call-interactively 'eww-reload)))
  50. ;;; consult narrowing
  51. (with-eval-after-load "consult"
  52. (defvar jao-eww-consult-history nil)
  53. (defvar jao-eww-buffer-source
  54. (list :name "eww buffer"
  55. :category 'eww-buffer
  56. :hidden t
  57. :narrow (cons ?e "eww")
  58. :annotate (lambda (c) (get-text-property 0 'url c))
  59. :history 'jao-eww-consult-history
  60. :action (lambda (b)
  61. (jao-afio-goto-www)
  62. (switch-to-buffer (get-text-property 0 'buffer b)))
  63. :items
  64. (lambda ()
  65. (seq-map (lambda (b)
  66. (with-current-buffer b
  67. (let ((tl (or (plist-get eww-data :title) ""))
  68. (url (or (eww-current-url) (buffer-name))))
  69. (propertize (if (string-blank-p tl) url tl)
  70. 'buffer b 'url url))))
  71. (seq-filter #'jao-www--buffer-p (buffer-list))))))
  72. (jao-consult-add-buffer-source 'jao-eww-buffer-source))
  73. ;;; images
  74. (defun jao-eww-next-image ()
  75. (interactive nil eww-mode)
  76. (when-let (p (text-property-search-forward 'image-displayer nil nil t))
  77. (goto-char (prop-match-beginning p))))
  78. ;;; close page and reopen
  79. (defvar jao-eww--closed-urls ())
  80. (defun jao-eww-close ()
  81. (interactive nil eww-mode)
  82. (when-let (current (eww-current-url))
  83. (add-to-list 'jao-eww--closed-urls current))
  84. (let ((nxt (car (jao-eww-session-invisible-buffers))))
  85. (kill-current-buffer)
  86. (when nxt (switch-to-buffer nxt nil t))))
  87. (defun jao-eww-reopen (arg)
  88. (interactive "P")
  89. (if (> (length jao-eww--closed-urls) 0)
  90. (let ((url (completing-read "URL: " jao-eww--closed-urls)))
  91. (jao-afio-goto-www)
  92. (setq jao-eww--closed-urls (remove url jao-eww--closed-urls))
  93. (eww url (when arg 4)))
  94. (message "No previously closed URLs.")))
  95. (defun jao-eww-reopen-new ()
  96. (interactive)
  97. (jao-eww-reopen t))
  98. ;;; sessions
  99. (use-package jao-eww-session
  100. :custom ((jao-eww-session-file "~/.emacs.d/cache/eww-session.eld")))
  101. ;;; eww to org
  102. (defun jao-eww-to-org (&optional dest)
  103. (interactive)
  104. (unless (org-region-active-p)
  105. (let ((shr-width 80)) (eww-readable)))
  106. (let* ((start (if (org-region-active-p) (region-beginning) (point-min)))
  107. (end (if (org-region-active-p) (region-end) (point-max)))
  108. (link (eww-current-url))
  109. (title (plist-get eww-data :title))
  110. (buff (save-current-buffer (or dest (jao-org-notes-create title)))))
  111. (with-current-buffer buff
  112. (insert "#+link: " link "\n\n")
  113. (org-mode))
  114. (save-excursion
  115. (goto-char start)
  116. (while (< (point) end)
  117. (let* ((p (point))
  118. (props (text-properties-at p))
  119. (k (seq-find (lambda (x) (plist-get props x))
  120. '(shr-url image-url outline-level face)))
  121. (prop (and k (list k (plist-get props k))))
  122. (next (if prop
  123. (next-single-property-change p (car prop) nil end)
  124. (next-property-change p nil end)))
  125. (txt (buffer-substring (point) next))
  126. (txt (replace-regexp-in-string "\\*" "·" txt)))
  127. (with-current-buffer buff
  128. (insert
  129. (pcase prop
  130. ((and (or `(shr-url ,url) `(image-url ,url))
  131. (guard (string-match-p "^http" url)))
  132. (let ((tt (replace-regexp-in-string "\n\\([^$]\\)" " \\1" txt)))
  133. (org-link-make-string url tt)))
  134. (`(outline-level ,n)
  135. (concat (make-string (- (* 2 n) 1) ?*) " " txt "\n"))
  136. ('(face italic) (format "/%s/ " (string-trim txt)))
  137. ('(face bold) (format "*%s* " (string-trim txt)))
  138. (_ txt))))
  139. (goto-char next))))
  140. (pop-to-buffer buff)
  141. (goto-char (point-min))))
  142. ;;; rdrview
  143. ;; https://jiewawa.me/2024/04/another-way-of-integrating-mozilla-readability-in-emacs-eww/
  144. (define-minor-mode eww-rdrview-mode
  145. "Toggle whether to use `rdrview' to make eww buffers more readable."
  146. :lighter " R"
  147. (if eww-rdrview-mode
  148. (progn
  149. (setq eww-retrieve-command '("rdrview" "-T" "title,sitename,body" "-H"))
  150. (add-hook 'eww-after-render-hook #'eww-rdrview-update-title))
  151. (progn
  152. (setq eww-retrieve-command nil)
  153. (remove-hook 'eww-after-render-hook #'eww-rdrview-update-title))))
  154. (defun eww-rdrview-update-title ()
  155. "Change title key in `eww-data' with first line of buffer.
  156. It should be the title of the web page as returned by `rdrview'"
  157. (save-excursion
  158. (goto-char (point-min))
  159. (plist-put eww-data :title (string-trim (thing-at-point 'line t))))
  160. (eww--after-page-change))
  161. (defun eww-rdrview-toggle-and-reload ()
  162. "Toggle `eww-rdrview-mode' and reload page in current eww buffer."
  163. (interactive)
  164. (if eww-rdrview-mode (eww-rdrview-mode -1)
  165. (eww-rdrview-mode 1))
  166. (eww-reload))
  167. ;;; auto-readable
  168. (defvar jao-eww-auto-readable-urls
  169. (regexp-opt '("guardian.co.uk" "theguardian.com" "github.com" "eldiario.es")))
  170. (defun jao-eww-autoread ()
  171. (when (string-match-p jao-eww-auto-readable-urls (or (eww-current-url)))
  172. (eww-readable)))
  173. (add-hook 'eww-after-render-hook #'jao-eww-autoread)
  174. ;;; package
  175. (use-package shr
  176. :custom ((shr-width nil)
  177. (shr-use-colors t)
  178. (shr-use-fonts nil)
  179. (shr-max-width 160)
  180. (shr-blocked-images nil)
  181. (shr-inhibit-images t)
  182. (shr-max-image-proportion 0.8)
  183. (shr-hr-line ?―)))
  184. (use-package eww
  185. :demand t
  186. :custom ((eww-browse-url-new-window-is-tab nil)
  187. (eww-download-directory jao-sink-dir)
  188. (eww-header-line-format " %u")
  189. (eww-form-checkbox-selected-symbol "☒")
  190. (eww-buffer-name-length 180)
  191. ;; (eww-readable-urls '("guardian\\.co\\.uk"
  192. ;; "theguardian\\.com"
  193. ;; "eldiario\\.es"
  194. ;; "theconversation"))
  195. )
  196. :config
  197. (with-eval-after-load "org" (require 'ol-eww nil t))
  198. (defun jao-eww-buffer-name ()
  199. (when-let ((s (or (plist-get eww-data :title)
  200. (plist-get eww-data :url))))
  201. (when (not (string-blank-p s)) (format "%s" s))))
  202. (setq eww-auto-rename-buffer #'jao-eww-buffer-name)
  203. (defun jao-eww-readable (rdrview)
  204. (interactive "P" eww-mode)
  205. (if rdrview
  206. (eww-rdrview-toggle-and-reload)
  207. (eww-readable)))
  208. :bind (:map eww-mode-map (("b" . eww-back-url)
  209. ("B" . eww-forward-url)
  210. ("d" . jao-download)
  211. ("f" . link-hint-open-link)
  212. ("F" . embark-on-link)
  213. ("L" . eww-forward-url)
  214. ("N" . jao-eww-next-image)
  215. ("o" . jao-eww-browse)
  216. ("O" . jao-eww-browse-new)
  217. ("r" . jao-eww-reload)
  218. ("R" . jao-eww-readable)
  219. ("s" . eww-search-words)
  220. ("S" . jao-eww-browse-new)
  221. ("T" . jao-mastodon-toot-url)
  222. ("u" . jao-eww-reopen)
  223. ("U" . jao-eww-reopen-new)
  224. ("w" . jao-eww-to-org)
  225. ("q" . jao-eww-close)
  226. ("x" . jao-rss-subscribe)
  227. ("y" . jao-eww-copy-link)
  228. ("\\" . eww-view-source)
  229. ("C-c C-w" . jao-eww-close)
  230. ("M-i" . eww-toggle-images))))
  231. ;;; fixes for shr image rendering
  232. (require 'shr)
  233. (defun jao-shr--kill-nl (p)
  234. (save-excursion
  235. (goto-char p)
  236. (when (looking-at-p "\n") (delete-char 1))))
  237. (defun jao-shr-tag-img (fn &rest args)
  238. (let ((p (point)))
  239. (prog1 (apply fn args)
  240. (when (> (point) p) (jao-shr--kill-nl p)))))
  241. (defun jao-shr-insert (fn &rest args)
  242. (let ((p (when (and (not (bolp))
  243. (get-text-property (1- (point)) 'image-url))
  244. (point))))
  245. (prog1 (apply fn args)
  246. (when (and p (> (point) p)) (jao-shr--kill-nl p)))))
  247. (advice-add 'shr-tag-img :around #'jao-shr-tag-img)
  248. (advice-add 'shr-insert :around #'jao-shr-insert)
  249. ;; (advice-remove 'shr-tag-img #'jao-shr-tag-img)
  250. ;; (advice-remove 'shr-insert #'jao-shr-insert)
  251. ;;; .
  252. (provide 'jao-custom-eww)