Resources

Org Roam: The Best Way to Keep a Journal in Emacs - YouTube From System Crafters

How I Take Notes with Org-roam

Manaul

Node Properties

这是 org-roam 最重要的设计

org-roam-ref

我之前一直不太理解这个东西的意义,直到我发现有 org-roam-protocol 这个东西
相当于可以可以直接从一个网页跳到一个节点!

添加和删除 <SPC m m o r/R>

也可以把这个东西当成 id 来跳转到节点

  • DONE 如何方便地跳回到网页呢?

    我自己写了一个 <my/open-roam-refs> 来解决了问题

The Org-roam Buffer

之后需要看看怎么配置 org-roam buffer 的行为

Extensions

org-roam-dailies

这是我使用的 daily-notes 工作流

doomemacs 下绑定到的是 (如果当前在 org-mode 下也可以按

这 [calendar](20250804023831-emacs.md#calendar-diary) 里的高亮真太不明显了

重新思考一下它存在的意义
我现在的设计是在这里放一些性质类似于杂题收集,比赛记录一样的东西

我不应该把我的那些有价值的值得回顾但是处于起步阶段的想法放在这里
我需要分裂我的 journal!

  • TODO 我希望他能起到和 一样的作用

    问题来了,我为什么不直接插入一个 timestamp 呢

org-roam-protocol

这里有比 org-protocol 的文档里更好的对 org-protocol 的说明

Insight

Convert current subtree at point to a node, and extract it into a new file.

org-roam-capture

Org-roam Basics: How org-roam-capture-templates Work | nobiot
Task management with org-roam Vol. 7: Capture - Boris Buliga

不知道为什么没有按照我的想法正确工作
他应该在对应节点的最后一个标题后面自动创建一个次级标题的
现在他只会跳转到对应节点所在的文件的正文第一行

原来这东西的功能和我想象的不一样。。。
我想的是他可以便利地跳转到某一个节点,然后像正常的 capture 一样插入文本

结果实际上是它的功能就是便利地插入节点的,
而且这个插入的位置不能基于节点树选择!这很不对
而且选择节点是为了插入节点的,我为什么需要一个id都完全重复的节点?

我想要的功能应该还是应该正常地用 org-capture 实现

我能不能做到在手动选择的节点下插入节点?

Examples

(push
 '("l" "log" entry
   "* ${title}\n\n%?"
   ;; Ensure to have a single node with this title/alias below.
   ;;            ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   :target (node "org-roam-capture")
   :unnarrowed t
   :prepend t
   :empty-lines 1)
 org-roam-capture-templates)

对默认模板的解释

要理解 org-roam-capture-templates 如何工作,让我们检查默认捕获模板。这个例子演示了六个元素的结构和目的,我们将逐步探讨。

("d"                                              ;; ➊
 "default"                                        ;; ➋
 plain                                            ;; ➌
 "%?"                                             ;; ➍
 :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" ;; ➎
                    "#+title: ${title}\n")
 :unnarrowed t)                                   ;; ➏

示例 5. 带编号元素的默认捕获模板

  1. 键(“d”) :选择此模板的快捷方式。
  2. 描述(“default”) :在提示中显示的标签。
  3. 类型(plain) :要插入的内容类型。
  4. 模板(”%?“) :要插入的文本内容。
  5. 目标 :文件名和标题内容。
  6. 可选属性 (:unnarrowed t):附加捕获行为。

➊ 键和 ➋ 描述

这两个元素, 键和*描述, 很简单。
key (“d”) 是选择模板的快捷方式,而描述(“default”)在您有多个捕获模板时显示在捕获提示中。例如,在下面的截图中,键(“d”和”e”)及其描述显示在捕获选择提示的底部供您选择。

➌ 类型和 ➍ 模板

*类型*(plain)是要插入的捕获内容的类型,而*模板*(”%?“)定义要插入的文本内容。“plain”类型按原样插入文本内容。默认模板”%?”没有文本内容,但”%?”告诉捕获在捕获过程后将光标放在此点。“entry”类型将文本内容作为子标题添加到现有节点下。例如,以下捕获模板将新标题添加到标题为”My Philosophy Log”的节点(示例 6)。

(push
 '("l" "log" entry
   "* ${title}\n\n%?"
   ;; 确保下面有一个具有此标题/别名的单个节点。
   ;;            ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   :target (node "My Philosophy Log")
   :unnarrowed t
   :prepend t
   :empty-lines 1)
 org-roam-capture-templates)
 

示例 6. 使用 node 作为目标的 entry 类型的示例捕获模板

如果节点是文件,新标题将是 1 级标题,如果节点是标题节点,新标题将是其直接子标题。如果您想尝试此示例,请使用 org-roam-capture 而不是 =org-roam-node-find=。要让它在您的 Emacs 中工作,请将目标节点替换为实际存在于您的 Org-roam 目录中的标题、别名或 ID。

为您的模板使用 file

在某些情况下,您可能希望将预定义的结构化内容插入到每个新节点中。使用文本文件作为模板可确保一致性并使维护可重用结构变得容易。这是一个将内联模板替换为外部文件内容的示例(示例 7)。

;; 可选地,通过将其设置为 nil 来重置 `org-roam-capture-templates`。您
;; 不需要这样做,但这使变量干净以进行下一次实验。
(setq  org-roam-capture-templates nil)
 
(push
 '("l" "log" entry
   (file "~/tmp/philosophy-log-capture.org")
   ;; 确保下面有一个具有此标题/别名的单个节点。
   ;;            ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   :target (node "My Philosophy Log")
   :unnarrowed t
   :prepend t
   :empty-lines 1)
 org-roam-capture-templates)
 

示例 7. 使用 file 作为模板的 entry 类型的示例捕获模板

模板文件 ~/tmp/philosophy-log-capture.org 看起来像这样(示例 8)。

* ${title}
 
** 你在想什么?
  %?
 
** 你对此有何感受?
 
** 还有其他评论吗?
 

示例 8. 文件 ~/tmp/philosophy-log-capture.org 的内容

:target 属性

目标 (示例 10)指定捕获的内容将放置在何处。在默认捕获模板中,它具有 file+head 选项的三个组成部分(示例 11):

  1. ➎-a 目标规范类型 ( file+head )。
  2. ➎-b 目标文件名 :包括时间戳(%<%Y%m%d%H%M%S>)和别名(${slug})。
  3. ➎-c 头部内容 :添加元数据,如 #+title。
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" ;; ➎
                    "#+title: ${title}\n")

示例 10. 默认捕获模板的 :target 属性

(file+head                      ;; ➎-a
 "%<%Y%m%d%H%M%S>-${slug}.org"  ;; ➎-b
 "#+title: ${title}\n")         ;; ➎-c

示例 11. :target 属性的元素

➎-a 目标规范类型

➎ 目标中的元素数量取决于*➎-a 目标规范类型*。在示例中我们有 file+head,因此它后面有两个元素:➎-b 和 ➎-c。文档字符串列出了 ➎-a 的可用选项,例如 file=、=file+olpnode,它们分别有一个、两个和一个元素跟随。

➎-b 目标文件名

"%<%Y%m%d%H%M%S>-${slug}.org"  ;; ➎-b

示例 12. ➎-b 目标文件名

➎-b 是*目标文件名*。您可以有绝对或相对文件名。如果是相对的,它相对于 =org-roam-directory=。在这个默认示例中,目标文件名以百分号(”%“)开头,后跟尖括号”<“。这是什么意思?查阅文档字符串,您将看到这部分。

此外,以下 %-转义将被替换为内容并展开

示例 13. 关于 %-转义的文档字符串

所以它是预定义的”%-转义”之一,它被展开并替换为其他东西。您还将看到可以使用的可用 %-转义列表,以尖括号开头的是这个。

%<...>      在 ... 格式规范上 'format-time-string' 的结果。

示例 14. %<…> format-time-string

我们如何使用它?默认模板中的尖括号包含此字符串 %Y%m%d%H%M%S,这是编程中格式化时间字符串的常见约定。有关更多详细信息,请参阅函数 format-time-string 的文档字符串。您可以像下面这样测试函数和时间字符串。在这里您正在评估 Emacs Lisp 表单或表达式。

(format-time-string "%Y%m%d%H%M%S" (current-time))
; => "20241020112615"

示例 15. 测试 format-time-string 的代码片段

然后时间戳后面跟着连字符”-“,用作时间戳和标题别名之间的分隔符。连字符可能埋在所有符号中,如”%”、”>”、”\$“和”{“,所有这些都具有某种语法意义,但连字符没有,因此将按原样作为字符逐字插入。

\${slug} 部分使用 Org-roam 特定的约定。在文档字符串的末尾,您将看到提到这一点。

Org-roam 在其模板中支持额外的替换。"${foo}"将在 Org-roam 节点中查找 foo 属性(参见 'org-roam-node')。

示例 16. Org-roam 的”${foo}“约定的文档字符串摘录

一开始可能不容易以技术精度理解这一点。现在不要太担心它。以下内容应该足以让您开始 - 我正在添加一些在上面的文档字符串摘录(示例 16)中不明确的上下文信息。

  • 您可以在整个当前捕获过程中使用正在捕获的节点的属性(有关完整列表,请参阅用户手册。=(info “(org-roam) Node Properties”)=)。

  • 您可以通过调用函数 org-roam-node-foo 来访问每个属性值(但请注意”foo”是一个虚构的示例,默认情况下在 Org-roam 中未定义)。

  • 默认情况下定义了几个属性,例如 slug=、=id=、=title 等。使用 describe-function 命令(默认绑定到”C-h f”)查看名称以 org-roam-node- 开头的函数。

当您在捕获过程中创建新节点时,标题是您在开始时为新节点输入的文本,您可以在捕获模板中使用 ${slug}=。在内部,捕获过程调用函数 =org-roam-node-slug 以基于标题返回别名。您可以使用下面的简单示例来更具体地了解它的工作原理。

(let* ((new-title (read-string "输入标题:"
                               "这是节点的标题"))
       (node (org-roam-node-create :title new-title)))
  (org-roam-node-slug node))
  例如 ⇒ "this_is_the_title_of_the_node"

示例 17. 测试 org-roam-node-slug 的代码片段

现在是 ${slug} 部分。在此之后,文件扩展名 .org 按原样添加到文件名的末尾。

我们将回到 ${slug} 约定,因为它使 org-roam-capture-templates 与其 Org 对应物 org-capture-templates 不同。我们可以使用此工具向节点添加自定义属性,但这是稍后的事情。

➎-c 头部内容

➎-c 是*头部内容*。它仅在首次创建文件时插入到文件中。

"#+title: ${title}\n")         ;; ➎-c

示例 18. 默认模板中的 ➎-c 头部内容

第一部分 #+title: 按字面添加。我们将在一分钟内讨论 ${title} 部分,但只是快速指出最后一部分 \n 的一件事。这在 Emacs 中添加了一个换行符,意味着它用作换行符。使用 \n,我们只是告诉捕获过程在此点结束该行,就像您在键盘上按下回车键(↵)一样。

注意 ${title} 部分。这与我们在上面的*➎-b 目标文件名*部分中看到的 ${slug} 使用的技术相同。这样,您可以在捕获过程中插入名为 titleslug 的节点属性。请参见下面的示例(示例 19)。

(let* ((new-title (read-string "输入标题:"
                               "这是节点的标题"))
       (node (org-roam-node-create :title new-title)))
  (org-roam-node-title node)) ;; <= 这次,这部分访问节点的标题槽
  例如 ⇒ "这是节点的标题"
 

示例 19. 测试 org-roam-node-title 的代码片段

➏ :unnarrowed 和其他可选属性

可选属性,如 :unnarrowed,提供对捕获过程的精细控制。例如,=:unnarrowed= 确保捕获缓冲区不限于缩小的视图,允许您查看笔记的完整上下文。有关完整列表,请参阅 org-roam-capture-templates 的文档字符串,并尝试使用这些选项来根据您的工作流程定制捕获行为。

我觉得我现在应该有能力直接读源码了

file link

基于 .dir-locals.el 的多 roam 库设置 基于.dir-locals的 capture template配置

Multiple Root directories - Development - Org-roam

一个可接受的应用是做项目笔记,可以在项目文件夹外套一层,在里面加一个 notes/ 文件夹

如何建立一个门户来串联多个 roam 库?
我觉得把跳转到另一个 roam 库的行为直接变为一个节点是很有意义的设计
然后我们应该可以配置一些库和库之间的链接,比如我通常会想要在 jj 库里访问 rust

还是说多库设置是不推荐的?应该是不推荐的。。。

可替代的解决方案

我们想要有基于 dir 的库的目的是方便跳转
其实应该用基于 .dir-locals.el 的 capture 配置实现

其实如果在特定目录的时候给 做预填充,
设置在特定节点下插入的 capture-template 的话,体验是更好的

具体信息可以看 .dir-locals

那么现在的问题就是,我们应该如何配置这两个东西了

从 logseq 迁移到 org-roam 的思考 ARCHIVE

Developing

The return value is a cons cell (file-name . position), or nil org-roam 的核心模块

关于tag的继承

我现在想要知道如何判定一个标签对于一个节点来说是不是继承得来的?

org-roam 不提供这个功能!!!这太离谱了
我觉得这明明很重要的

那只能看看能不能通过更底层的 org-mode 解决问题了
也就是说先用 roam 找到所有备选点,然后用 org-mode 的 org-get-tags 一一确定那个属性是不是继承得来的

在这里看看有没有解决方案吧
解决了!

(defun my/org-roam-nodes-with-local-tag (tag)
  "Return org-roam nodes where TAG is locally defined (non-inherited)."
  ;; (require 'org-roam)
  (let* ((rows (org-roam-db-query
                [:select [nodes:file nodes:pos]
                 :from nodes
                 :inner :join tags :on (= nodes:id tags:node_id)
                 :where (= tags:tag $s1)]
                tag))
         (result '()))
    (dolist (row rows result)
      (let ((file (nth 0 row))
            (pos  (nth 1 row))
            node)
        (with-current-buffer (find-file-noselect file)
          (save-excursion
            (goto-char pos)
            (setq node (org-roam-node-at-point t))
            (setq local-tags
                  (if (eq pos 1)
                      org-file-tags
                    (org-get-tags nil t)))
            (when (member tag local-tags)
              (push node result))))))
    result))
 
(my/org-roam-nodes-with-local-tag "CP")

如何使用

Run SQL query on Org-roam database with ARGS.
SQL can be either the emacsql vector representation, or a string.

可以做几个练习

当前的数据库模式,其实很好看懂!

((files
  [(file :unique :primary-key) title (hash :not-null) (atime :not-null)
   (mtime :not-null)])
 (nodes
  ([(id :not-null :primary-key) (file :not-null) (level :not-null)
    (pos :not-null) todo priority (scheduled text) (deadline text) title
    properties olp]
   (:foreign-key [file] :references files [file] :on-delete :cascade)))
 (aliases
  ([(node-id :not-null) alias]
   (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
 (citations
  ([(node-id :not-null) (cite-key :not-null) (pos :not-null) properties]
   (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
 (refs
  ([(node-id :not-null) (ref :not-null) (type :not-null)]
   (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
 (tags
  ([(node-id :not-null) tag]
   (:foreign-key [node-id] :references nodes [id] :on-delete :cascade)))
 (links
  ([(pos :not-null) (source :not-null) (dest :not-null) (type :not-null)
    (properties :not-null)]
   (:foreign-key [source] :references nodes [id] :on-delete :cascade))))

此处的 olp 表示 outline path
数据库中不会存直接的父子关系!

ISSUES

TODO 在裸双括号链接找不到对应标题的时候自动转换为 roam: 链接

灵感来源自 logseq 对链接的自动处理