From b917a65585691f21847821e0a042de1f4e75de42 Mon Sep 17 00:00:00 2001 From: Cytrogen Date: Wed, 11 Mar 2026 19:22:36 -0400 Subject: [PATCH] =?UTF-8?q?feat(reading):=20=E6=B7=BB=E5=8A=A0=20URL=20?= =?UTF-8?q?=E9=98=85=E8=AF=BB=E6=A8=A1=E5=BC=8F=E5=92=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=20EPUB/PDF=20=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正 Copyright 年份为 2026 - 新增 URL 阅读模式:C-x C-f 直接打开 URL,自动启用 olivetti 居中 + visual-line 排版 - 添加 my/toggle-reading-mode 手动切换阅读模式 - EPUB 阅读(nov.el)自动启用 olivetti 居中 - 添加 olivetti 包安装 --- config/pkg-reading.el | 122 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/config/pkg-reading.el b/config/pkg-reading.el index cf4eaffd351eb707230a1cd91c585900c3952b39..5891b732c97998f05df9ff086826cdf5b2aefe46 100644 --- a/config/pkg-reading.el +++ b/config/pkg-reading.el @@ -1,6 +1,6 @@ ;;; pkg-reading.el --- RSS reader, OPDS browser, and ebook tools -*- lexical-binding: t -*- -;; Copyright (C) 2024 Cytrogen +;; Copyright (C) 2026 Cytrogen ;;; Commentary: @@ -52,6 +52,9 @@ (package-refresh-contents) (package-install 'elfeed)) +(unless (package-installed-p 'olivetti) + (package-install 'olivetti)) + (unless (package-installed-p 'elfeed-protocol) (package-install 'elfeed-protocol)) @@ -61,6 +64,45 @@ (unless (package-installed-p 'pdf-tools) (package-install 'pdf-tools)) +;;; ============================================================ +;;; URL Reading Mode +;;; ============================================================ + +;; 启用 url-handler-mode,可以直接 C-x C-f 打开 URL +(url-handler-mode 1) + +;; 配置 olivetti 居中宽度 +(setq olivetti-body-width 80) + +(defun my/url-buffer-p () + "Check if current buffer was opened from a URL." + (and buffer-file-name + (string-match-p "^https?://" buffer-file-name))) + +(defun my/setup-url-reading-mode () + "Setup comfortable reading mode for URL buffers." + (when (my/url-buffer-p) + (visual-line-mode 1) + (olivetti-mode 1) + (read-only-mode 1) + (message "URL reading mode enabled"))) + +;; 打开 URL 文件时自动启用阅读模式 +(add-hook 'find-file-hook #'my/setup-url-reading-mode) + +;; 手动切换阅读模式的命令 +(defun my/toggle-reading-mode () + "Toggle reading mode (visual-line + olivetti)." + (interactive) + (if olivetti-mode + (progn + (visual-line-mode -1) + (olivetti-mode -1) + (message "Reading mode disabled")) + (visual-line-mode 1) + (olivetti-mode 1) + (message "Reading mode enabled"))) + ;;; ============================================================ ;;; EPUB Reader (nov.el) ;;; ============================================================ @@ -70,7 +112,8 @@ (with-eval-after-load 'nov (setq nov-text-width t) - (add-hook 'nov-mode-hook 'visual-line-mode)) + (add-hook 'nov-mode-hook 'visual-line-mode) + (add-hook 'nov-mode-hook 'olivetti-mode)) ;;; ============================================================ ;;; PDF Reader (pdf-tools) @@ -135,6 +178,23 @@ (elfeed) (elfeed-update)) +;;; ============================================================ +;;; elfeed + eww Integration +;;; ============================================================ + +(defun my/elfeed-show-eww () + "View current elfeed article in eww browser." + (interactive) + (let ((link (elfeed-entry-link elfeed-show-entry))) + (when link + (eww link)))) + +(with-eval-after-load 'elfeed-show + (define-key elfeed-show-mode-map (kbd "b") #'my/elfeed-show-eww)) + +(add-hook 'eww-mode-hook #'visual-line-mode) +(add-hook 'eww-mode-hook #'olivetti-mode) + ;;; ============================================================ ;;; OPDS Browser (Calibre) ;;; ============================================================ @@ -173,8 +233,19 @@ (prog1 (libxml-parse-xml-region (point) (point-max)) (kill-buffer)))))) +(defvar my/opds--next-page-url nil + "URL for the next page of OPDS results.") + (defun my/opds--parse-entries (xml) "Parse OPDS XML and return list of entries." + ;; 解析下一页链接 + (let ((links (dom-by-tag xml 'link))) + (setq my/opds--next-page-url + (dom-attr (cl-find-if (lambda (link) + (string= "next" (dom-attr link 'rel))) + links) + 'href))) + ;; 解析书籍条目 (let ((entries (dom-by-tag xml 'entry))) (mapcar (lambda (entry) (let* ((title (dom-text (dom-by-tag entry 'title))) @@ -237,9 +308,20 @@ (let* ((choices (mapcar (lambda (e) (cons (my/opds--format-entry e) e)) entries)) - (selection (completing-read "OPDS: " choices nil t))) + ;; 如果有下一页,添加选项 + (choices (if my/opds--next-page-url + (append choices '(("▶ 下一页..." . :next-page))) + choices)) + (selection (completing-read + (format "OPDS (%d items%s): " + (length entries) + (if my/opds--next-page-url " + more" "")) + choices nil t))) (when selection - (my/opds--select-entry (cdr (assoc selection choices)))))) + (let ((selected (cdr (assoc selection choices)))) + (if (eq selected :next-page) + (my/opds--browse-url my/opds--next-page-url) + (my/opds--select-entry selected)))))) (error (message "OPDS error: %s" (error-message-string err)))))) (defun my/opds-browse () @@ -298,7 +380,8 @@ (my/reading--load-config) (let* ((query (read-string "Search books: ")) (base-url (my/reading--get-config :opds :url)) - (search-url (format "%s/search?query=%s" + ;; Calibre 搜索格式: /opds/search/关键词 + (search-url (format "%s/search/%s" (replace-regexp-in-string "/opds/?$" "/opds" base-url) (url-hexify-string query)))) (my/opds--browse-url search-url))) @@ -315,5 +398,34 @@ (my/reading--setup-elfeed)) (message "Reading configuration reloaded")) +;;; ============================================================ +;;; PDF Tools Enhancement +;;; ============================================================ + +;; PDF 页码偏移量 +(defvar-local my/pdf-page-offset 0 + "当前 PDF 的页码偏移量。书籍页码 = 物理页码 - 偏移量") + +(defun my/pdf-set-offset () + "设置当前 PDF 的页码偏移量。" + (interactive) + (let* ((current-page (pdf-view-current-page)) + (book-page (read-number (format "当前是 PDF 第 %d 页,对应书籍第几页?" current-page))) + (offset (- current-page book-page))) + (setq my/pdf-page-offset offset) + (message "偏移量设为 %d(书籍第1页 = PDF第%d页)" offset (1+ offset)))) + +(defun my/pdf-goto-page () + "跳转到书籍页码(自动应用偏移量)。" + (interactive) + (let* ((book-page (read-number "跳转到书籍第几页: ")) + (pdf-page (+ book-page my/pdf-page-offset))) + (pdf-view-goto-page pdf-page))) + +;; 绑定快捷键 +(with-eval-after-load 'pdf-view + (define-key pdf-view-mode-map (kbd "O") 'my/pdf-set-offset) + (define-key pdf-view-mode-map (kbd "G") 'my/pdf-goto-page)) + (provide 'pkg-reading) ;;; pkg-reading.el ends here