From 2274abff2c30596eb2b0f0d484edee5b2013e303 Mon Sep 17 00:00:00 2001 From: Cytrogen Date: Sat, 17 Jan 2026 22:02:31 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B9=A6=E5=8D=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=92=8C=20org-agenda=20=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E8=A7=86=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - org-structure.org: 添加"想读"和"读完"两个 capture 模板 - pkg-org.el: 添加 org-agenda 自定义视图 (C-c a r) - rw: 查看想读的书 - rb: 查看正在读的书 - rd: 查看读完的书 - is: 查看分享的项目 - pkg-org.el: 添加书单操作快捷键 - S: 开始阅读(从想读转为书籍笔记) - F: 完成阅读(创建读后感) --- config/pkg-org.el | 93 +++++++++++++++++++++++++++++++++++++++++++++++ org-structure.org | 12 ++++++ 2 files changed, 105 insertions(+) diff --git a/config/pkg-org.el b/config/pkg-org.el index 9e307c66406337bcab70648c73260ffd68f039bc..68a4ba597c8a065d37cc34b2a17e07b28627a80b 100644 --- a/config/pkg-org.el +++ b/config/pkg-org.el @@ -139,6 +139,99 @@ Format: ((key label file ((subkey sublabel headline template override-file) ...) ;; Org Workflow Configuration ;; Agenda, refile, and capture setup (with-eval-after-load 'org + ;; --- Custom Agenda Views --- + ;; 将"想读"的书转为"正在读"(创建书籍笔记并删除原条目) + (defun my/reading-start-book () + "Start reading a book: create a note entry and remove from wishlist." + (interactive) + (org-agenda-check-type t 'agenda 'tags) + (let* ((marker (or (org-get-at-bol 'org-marker) + (org-agenda-error))) + (buffer (marker-buffer marker)) + (pos (marker-position marker)) + book-name) + ;; 获取书名 + (with-current-buffer buffer + (goto-char pos) + (setq book-name (org-get-heading t t t t))) + ;; 确认操作 + (when (y-or-n-p (format "开始阅读《%s》?" book-name)) + ;; 删除原条目 + (org-agenda-kill) + ;; 创建书籍笔记 + (let ((org-capture-templates + `(("b" "书籍笔记" entry + (file+headline ,(expand-file-name "reading.org" org-directory) "书籍笔记") + ,(format "** 《%s》笔记 %%^{序号}: %%^{标题}\n%%U\n\n%%?" book-name))))) + (org-capture nil "b"))))) + + ;; 将"正在读"的书标记为"读完"(创建读后感) + (defun my/reading-finish-book () + "Finish reading a book: create a review entry." + (interactive) + (org-agenda-check-type t 'agenda 'tags) + (let* ((marker (or (org-get-at-bol 'org-marker) + (org-agenda-error))) + (buffer (marker-buffer marker)) + (pos (marker-position marker)) + book-name) + ;; 从标题中提取书名(格式:《书名》笔记 X: 标题) + (with-current-buffer buffer + (goto-char pos) + (let ((heading (org-get-heading t t t t))) + (if (string-match "《\\([^》]+\\)》" heading) + (setq book-name (match-string 1 heading)) + (setq book-name (read-string "书名: "))))) + ;; 创建读后感 + (let ((org-capture-templates + `(("d" "读后感" entry + (file+headline ,(expand-file-name "reading.org" org-directory) "读完") + ,(format "** 《%s》读后感\n:PROPERTIES:\n:FINISHED: %%U\n:END:\n\n%%?" book-name))))) + (org-capture nil "d")))) + + ;; 辅助函数:跳过不在指定父标题下的条目 + (defun my/org-agenda-skip-unless-parent (parent-headline) + "Skip entry unless its parent headline matches PARENT-HEADLINE." + (let ((dominated nil)) + (save-excursion + (while (and (not dominated) (org-up-heading-safe)) + (when (string= (org-get-heading t t t t) parent-headline) + (setq dominated t)))) + (if dominated + nil ; 不跳过 + (save-excursion (org-end-of-subtree t))))) ; 跳过 + + (setq org-agenda-custom-commands + '(;; Reading lists + ("r" . "Reading (书单)") + ("rw" "想读的书" tags "LEVEL=2" + ((org-agenda-files (list (expand-file-name "reading.org" org-directory))) + (org-agenda-overriding-header "想读的书") + (org-agenda-prefix-format " ") + (org-agenda-skip-function '(my/org-agenda-skip-unless-parent "想读")))) + ("rb" "正在读 (书籍笔记)" tags "LEVEL=2" + ((org-agenda-files (list (expand-file-name "reading.org" org-directory))) + (org-agenda-overriding-header "正在读的书") + (org-agenda-prefix-format " ") + (org-agenda-skip-function '(my/org-agenda-skip-unless-parent "书籍笔记")))) + ("rd" "读完的书" tags "LEVEL=2" + ((org-agenda-files (list (expand-file-name "reading.org" org-directory))) + (org-agenda-overriding-header "读完的书") + (org-agenda-prefix-format " ") + (org-agenda-skip-function '(my/org-agenda-skip-unless-parent "读完")))) + ;; Inbox items + ("i" . "Inbox") + ("is" "分享项目" tags "LEVEL=2" + ((org-agenda-files (list (expand-file-name "inbox.org" org-directory))) + (org-agenda-overriding-header "分享的项目/工具") + (org-agenda-prefix-format " ") + (org-agenda-skip-function '(my/org-agenda-skip-unless-parent "分享")))))) + + ;; Agenda 快捷键:书单操作 + (with-eval-after-load 'org-agenda + (define-key org-agenda-mode-map (kbd "S") #'my/reading-start-book) + (define-key org-agenda-mode-map (kbd "F") #'my/reading-finish-book)) + ;; --- Refile Configuration --- ;; 强化 Refile 功能,允许将条目移动到项目或分类的具体标题下 (setq org-refile-use-outline-path 'file) diff --git a/org-structure.org b/org-structure.org index 6b978247348264e659435aa86b33c6ee062a9427..b2997ef2fbd0948f91afe94c16bf0a4fc31849a6 100644 --- a/org-structure.org +++ b/org-structure.org @@ -182,12 +182,24 @@ :HEADLINE: Inbox :TEMPLATE: * TODO Read: %^{Title}\n :PROPERTIES:\n :SOURCE: %^{Source|Web|Book|Paper|Other}\n :URL: %^{Url}\n :ADDED: %U\n :END:\n %? :END: +** 想读 + :PROPERTIES: + :KEY: w + :HEADLINE: 想读 + :TEMPLATE: ** %^{书名}\n:PROPERTIES:\n:SOURCE: %^{来源}\n:ADDED: %U\n:END:\n%? + :END: ** 书籍笔记 :PROPERTIES: :KEY: b :HEADLINE: 书籍笔记 :TEMPLATE: ** 《%^{书名}》笔记 %^{序号}: %^{标题}\n%U\n\n%? :END: +** 读完 + :PROPERTIES: + :KEY: d + :HEADLINE: 读完 + :TEMPLATE: ** 《%^{书名}》读后感\n:PROPERTIES:\n:FINISHED: %U\n:END:\n\n%? + :END: ** 课程笔记 :PROPERTIES: :KEY: c