Vandee's Blog

09 May 2026

极简浏览器也可以在 Emacs

Intro

很久以前,我只知道浏览器是可以打开网页的一个东西。随着对代码的学习,现在才真正了解到它是什么。

我不是特别在乎互联网隐私,而且一些对我无关紧要的数据,如果对提升产品和用户体验有帮助,我甚至很乐于提供。

但随着 AI 的发展,互联网的这些变得越来越没有原则,产品和用户之间的信任也越来越差。我没有什么特别的网络用户隐私要保护,隐私也从来只是一个相对概念。我也没有想着这些环境会变得很好,只是觉得至少我可以做出一些选择,而不只是当问题发生在自己身上的时候抱怨。

浏览器,JavaScript,Node.js,HTML,WebAssembly,WebView,这些相关实现在我现在的代码认知里,很不优雅。特别是混乱的 JavaScript 和前端体系。我慢慢了解了一些相关的发展历史,这些本身在底层就存在着不可能修复的 “漏洞”,太纠结隐私没啥必要。

或许 AI 会发展出用 token 来传递信息的新互联网传输协议? 浏览器这种实现路径说不定再过十年真的会大变样。用户不再需要受到前端框架、环境的限制,内容和信息的传递成为核心,当然这个成本是很大的。有了 AI 之后,打开浏览器这个动作已经被各种 agent 在慢慢替代。TUI 和 CLI 现在是我用的最多的 UI 了。界面就是和用户意图之间的交互,用户 UI 和 interface 在以后一定会发生天翻地覆的变化,现在的 GUI 底层框架逻辑,我觉得承载不了这些交互,也很没有未来感。扯远了。

现在的互联网优质内容越来越少,日常使用里,智能手机之后网页的访问也慢慢被 app 替代(我一直更喜欢网页而不是 app),我需要在浏览器里主动访问的网页内容也变得越来越少。现在基本只剩下 LobstersHacker News 等论坛(因为我需要翻译插件!),还有 karakeep。

这些文本信息偏多的内容,一直以来也不太依赖浏览器。

Emacs 配置

Emacs is my browser - The Universe of Joshua Blais 偶然看到这篇文章。

和作者一样 Lynx (web browser), Browsh, nyxt 我也都尝试过。

之前只是稍微尝试了一下 eww,没想到稍微多配置一下就完全可以满足我现在的需求了!

网页用 eww,视频用 mpv,PDF 在 Emacs 里也可以直接看。

越来越觉得,all you need is a text editor.

根据作者的文章,下面是具体配置:

;; eww
;; 禁止eww生成cookie
;; https://github.com/lujun9972/lujun9972.github.com/blob/source/Emacs%E4%B9%8B%E6%80%92/%E5%A6%82%E4%BD%95%E7%A6%81%E6%AD%A2eww%E7%94%9F%E6%88%90cookie.org
(setq url-cookie-trusted-urls '()        ;不设置白名单
      url-cookie-untrusted-urls '(".*")  ;所有内容都匹配黑名单
      eww-readable-urls '(".*"))         ;默认开启 readable

;; https://joshblais.com/blog/emacs-as-my-browser/
(setq eww-search-prefix "https://duckduckgo.com/search?q=")
(setq eww-download-directory (expand-file-name "~/Downloads/"))

(defun my-browse-url-mpv (url &rest _args)
  "Open URL in mpv."
  (start-process "mpv" nil "mpv" url))

(defun my-browse-url-pdf (url &rest _args)
  "Fetch remote PDF and open in pdf-tools within Emacs."
  (let ((tmp (make-temp-file "emacs-pdf-" nil ".pdf")))
    (url-copy-file url tmp t)
    (find-file-other-window tmp)
    (pdf-view-mode)))

(setq browse-url-handlers
      '(("\\(youtube\\.com\\|youtu\\.be\\|vimeo\\.com\\|twitch\\.tv\\|bilibili\\.com\\|b23\\.tv\\)" . my-browse-url-mpv)
        ("\\.mp4$" . my-browse-url-mpv)
        ("\\.pdf$" . my-browse-url-pdf)
        ("." . eww-browse-url)))

;; Keep your fallback setting
(setq browse-url-secondary-browser-function 'browse-url-generic
      browse-url-generic-program "librewolf")

(with-eval-after-load 'eww
  (define-key eww-mode-map (kbd "=") #'text-scale-increase)
  (define-key eww-mode-map (kbd "-") #'text-scale-decrease)
  (define-key eww-mode-map (kbd "0") #'text-scale-adjust))

(setq shr-width 100)
(setq shr-max-width 120)
(setq shr-indentation 4)

(setq shr-use-fonts nil)
(setq shr-max-image-size '(800 . 600))
(setq shr-image-animate t)

(defun my/eww-download-image-at-point ()
  "Download image at point to `eww-download-directory'."
  (interactive)
  (let ((url (or (get-text-property (point) 'image-url)
                 (get-text-property (point) 'shr-url))))
    (if (not url)
        (message "No image at point")
      (let* ((filename (file-name-nondirectory (url-filename (url-generic-parse-url url))))
             (dest (expand-file-name filename eww-download-directory)))
        (url-copy-file url dest t)
        (message "Saved: %s" dest)))))

;; Keybinds
(with-eval-after-load 'eww
  (define-key eww-mode-map (kbd "b") #'eww-back-url)
  (define-key eww-mode-map (kbd "a") #'eww-add-bookmark)
  (define-key eww-mode-map (kbd "U") #'shr-copy-url)
  (define-key eww-mode-map (kbd "D") #'my/eww-download-image-at-point))

(with-eval-after-load 'evil
  (evil-define-key 'normal eww-mode-map
    (kbd "&") #'eww-browse-with-external-browser))

Tags: Emacs Ramble