Skip to content

A notetaking system like Roam using Emacs Org-mode

License

Notifications You must be signed in to change notification settings

meedstrom/org-node

Repository files navigation

org-node

News for v2.0

Packaging status

Version 2.0 is out, with two major features:

  • backlink drawers!
  • context buffers!

Feedback welcome.

(UPDATE: v2.2.1 fixes the backlink drawers, didn’t quite work right and could trigger an infinite loop freezing Emacs)

Upgrade notice

  • Emacs 29+ required
  • If you were using org-node 1.8.7 or older (November 2024), you’ll have a smoother ride if you:
    1. first upgrade to the latest in v1.9 branch (with dependency el-job v0.3),
    2. deal with any transitional warnings,
    3. then upgrade to the latest version (which is the main branch).

    Or skip all that, and just check that you’re not using any of these aliases.

Backlinks drawer

Users of org-node-backlink-mode can now choose whether it should generate multiline drawers or the old default, a single property line.

These drawers are inspired by org-super-links. To demonstrate, instead of this (a property line):

* My problems in life
:PROPERTIES:
:CREATED: [2023-Dec-25 17:20]
:ID: 4323245
:BACKLINKS: [[id:123456][Relationships]] [[id:235611][Feelings]]
:END:

Blah blah blah

you can now have this (a drawer):

* My problems in life
:PROPERTIES:
:CREATED: [2023-Dec-25 17:20]
:ID: 4323245
:END:
:BACKLINKS:
[2025-Jan-05 01:57] <- [[id:123456][Relationships]]
[2025-Feb-26 10:29] <- [[id:235611][Feelings]]
:END:

Blah blah blah

The choice is controlled by variable org-node-backlink-do-drawers, which defaults to t.

To transition all files, you can run these two commands:

M-x org-node-backlink-mass-delete-props
M-x org-node-backlink-mass-update-drawers

See what can be customized:

M-x customize-group RET org-node-backlink

Context buffers

The new context buffer replaces org-roam buffer, and with it, much of org-node-fakeroam.

Some direct substitutes:

NewOld (dependent on org-roam)
org-node-context-raiseorg-node-fakeroam-show-buffer
org-node-context-toggleorg-roam-buffer-toggle
org-node-context-follow-modeorg-node-fakeroam-redisplay-mode
org-node-context-persist-on-diskorg-node-fakeroam-fast-render-persist

You no longer need these:

  • Minor mode org-node-fakeroam-jit-backlinks-mode
  • Minor mode org-node-fakeroam-fast-render-mode

MISSING features for now:

  • org-roam’s “node-dedicated” buffers
  • grep sections (but see command org-node-grep)

See what can be customized:

M-x customize-group RET org-node-context

Incompatible changes

  1. Dropped support for Emacs 28.
    • Discovered that it likely has not worked for a while anyway.
    • If you’re on Emacs 28, you could try org-node v1.9 (with dependency el-job v0.3 on latest commit within that branch), but no guarantees. Or—since you’re probably a Debian user—get Emacs 30 which recently landed in Debian trixie.
  2. Removed a number of deprecated aliases.
  3. Bumped dependency el-job to the just-released v1.0.
  4. Struct type org-node-link removed.
    • A link is now a plist, so use e.g. (plist-get LINK :pos) instead of (org-node-link-pos LINK).
  5. New option org-node-backlink-do-drawers implies a different default behavior from before, but a message will be printed to help you out.
    • Default was changed because timestamps are hard to reconstruct later – so it’s better that they are opt-out than opt-in.

Background

What’s all this

I like org-roam but found it too slow, so I made quickroam. And that idea spun off into this package, a standalone thing. It may also be easier to pick up than org-roam.

  • If you were using org-roam, there is nothing to migrate. You can use both packages. It’s the same on-disk format: “notes” are identified by their org-id.

    With optional shims, you can even skip syncing the org-roam DB and continue using its rich backlinks buffer!

    In pursuit of being “just org-id”, this package has no equivalent setting to org-roam-directory – it just looks up the org-id-locations table.

  • If you were not using org-roam, maybe think of it as somewhat like org-recent-headings tooled-up to the extent that you won’t need other methods of browsing, as long as you give IDs to all objects of interest.

    If you were the sort of person to prefer ID-links over file links or any other type of link, you’re in the right place! Now you can rely on IDs, and—if you want—stop worrying about filenames, directories and subtree hierarchies. As long as you’ve assigned an ID to a heading or file, you can find it later.

What’s a “node”?

My life can be divided into two periods ”before org-roam” and ”after org-roam”. I crossed a kind of gap once I got a good way to link between my notes. It’s odd to remember when I just relied on browsing subtrees and filesystem directories – what a strange way to work!

I used to lose track of things I had written, under some forgotten heading in a forgotten file in a forgotten directory. The org-roam method let me find and build on my own work, instead of recreating it all the time.

At the core, all the “notetaking packages” (orgrr/zk/zetteldeft/org-roam/denote/howm/minaduki/…) try to help you with this: make it easy to link between notes and explore them.

Right off the bat, that imposes two requirements: a method to search for notes, since you can’t link to something you can’t search for, and a design-choice about what kinds of things should turn up as search hits. What’s a “note”?

Just searching for Org files is too coarse. Just searching for any subtree anywhere brings in too much clutter.

Here’s what org-roam invented. It turns out that if you limit the search-hits to just those files and subtrees you’ve deigned to assign an org-id – which roughly maps to everything you’ve ever thought it was worth linking to – it filters out the noise excellently.

Once a subtree has an ID you can link to, it’s a “node” because it has joined the wider graph, the network of linked nodes. I wish the English language had more distinct sounds for the words “node” and “note”, but to clarify, I’ll say “ID-node” when the distinction matters.

Features

A comparison of three systems that all permit relying on org-id and don’t lock you into the concept of “one-note-per-file”.

Featureorg-roamorg-nodeorg-super-links
Backlinksyesyesyes
Node search and insertyesyes– (suggests org-ql)
Node aliasesyesyes
Node exclusionyeslimitednot applicable
Refileyesyes
Rich backlinks bufferyesyes
Customize how backlinks shownyesyesyes
Reflinksyesyes (as backlinks)
Ref searchyesyes (as aliases)not applicable
Org 9.5 @citations as refsyesyesnot applicable
Support org-ref v3yeslimitednot applicable
Support org-ref v2yesnot applicable
Work thru org-roam-captureyesyes?
Work thru org-captureyes?
Daily-nodesyesyes
Node sequencesyes
Show backlinks in same windowyesyes
Cooperate with org-super-linksyesnot applicable
Fix link descriptionsyes
List dead linksyes
Rename file when title changesyes
Warn about duplicate titlesyes
Principled “related-section”yesyes
Untitled notes
Support roam: linksyes– (wontfix)
Can have separate note pilesyes– (wontfix)not applicable
Some query-able cacheEmacSQLhash tables
Async cache rebuildyesnot applicable
Time to cache my 3000 nodes2m 48s0m 01snot applicable
Time to save file w/ 400 nodes5–10sinstant?
Time to open minibuffer1–3sinstantnot applicable

Setup

Install

Assuming your package manager knows about MELPA, add this initfile snippet:

(use-package org-node
  :after org
  :config (org-node-cache-mode))

If you use org-roam, you may want the following module as well, at least during a transition period. Check its README to make org-node work smoothly with org-roam!

(use-package org-node-fakeroam
  :defer)

An update broke things?

See How to rollback at the end of this readme.

Quick start

If you’re new to these concepts, fear not. The main things for day-to-day operation are two verbs: “find” and “insert”.

Pick some short keys and try them out.

(keymap-set global-map "M-s M-f" #'org-node-find)
(keymap-set org-mode-map "M-s M-i" #'org-node-insert-link)

To browse config options, type M-x customize-group RET org-node.

Final tip: there’s no separate command for creating a new node! Reuse one of the commands above, and type the name of a node that doesn’t exist.

Try it and see what happens!

Backlinks

Backlinks are the butter on the bread that is your notes. If you’ve ever seen a “What links here” section on some webpage, that’s exactly what it is. Imagine seeing that, all the time. The following sections outline two general ways to do so.

Backlink solution 1: displayed in a separate window

Option 1A: Reuse the org-roam buffer

Believe it or not, it Just Works. So long as you install org-node-fakeroam – which pulls in org-roam with it – and add to initfiles:

(org-node-fakeroam-redisplay-mode)
(org-node-fakeroam-jit-backlinks-mode) ;; shim so no SQLite needed
(org-node-fakeroam-fast-render-mode) ;; perf fixes

;; Example keybindings
(keymap-set org-mode-map "M-s M-t M-t" #'org-roam-buffer-toggle)
(keymap-set org-mode-map "M-s M-t M-d" #'org-roam-buffer-display-dedicated)
;; if your window mgmt style is constantly lose all windows
(keymap-set global-map "M-s M-r" #'org-node-fakeroam-show-buffer)

Option 2A: Use the new org-node-context buffer

Org-node ships a complete rewrite of the org-roam buffer.

Add to initfiles:

(org-node-context-follow-mode)

;; Example keybindings
(keymap-set org-mode-map "M-s M-t" #'org-node-context-toggle)
;; if your window mgmt style is constantly lose all windows
(keymap-set global-map "M-s M-r" #'org-node-context-raise)

Backlink solution 2: Print inside the file

I rarely have the screen space to display a backlink buffer. Because it needs my active involvement to keep visible, I go long periods seeing no backlinks.

A complementary solution, which can also stand alone, is to have the backlinks written into the file, on an Org property line or in a drawer.

Option 2A: Automatic :BACKLINKS: property

Add to initfiles:

(setq org-node-backlink-do-drawers nil)
(org-node-backlink-mode)

For a first-time run, type M-x org-node-backlink-mass-update-props. (Don’t worry if you change your mind; undo with M-x org-node-backlink-mass-delete-props.)

NOTE 1: To be clear, this mode never generates new IDs. That’s your own business. This only adds/edits :BACKLINKS: properties.

That also means that not all links create a backlink, only links located in an entry that has an ID. After all, there must be something to link back to.

NOTE 2: People who prefer to hard-wrap text, instead of enabling visual-line-mode or similar, may prefer Option 2B.

Option 2B: Automatic :BACKLINKS:...:END: drawer

Same as Option 2A, but uses a multiline drawer.

For a first-time run, type M-x org-node-backlink-mass-update-drawers. (Don’t worry if you change your mind; undo with M-x org-node-mass-delete-drawers.)

Then add to initfiles:

(org-node-backlink-mode)

Option 2C: Semi-automatic :BACKLINKS:...:END: drawer

If you were previously using org-super-links, you can continue letting it manage them, and leave org-node out of the matter.

Just add to initfiles:

(add-hook 'org-node-insert-link-hook
          #'org-super-links-convert-link-to-super)

Now, you have some bonus tools:

  • 1. You can list any dead forward-links to fix them manually:

    M-x org-node-list-dead-links

  • 2. You can add all missing backlinks in bulk:

    M-x org-node-backlink-mass-update-drawers

The second command may be useful as a starting point if you’re new to org-super-links, pre-populating the notes you already have.

However, when you have pre-existing drawers… MAKE A FULL BACKUP before trying it!

Org-node has a different usage in mind than org-super-links. You may be accustomed to having old manually formatted and sorted drawers.

Running aforementioned command may re-sort your backlinks and re-format their appearance into something you don’t want; double-check the following options:

  • org-node-backlink-drawer-sorter
  • org-node-backlink-drawer-formatter

Finally, lines without a proper Org link like [[id:foo][bar]] are deleted, which would mean destroying any timestamp within.

Optional: Fully replace org-roam

There’s a rich ecosystem of extensions to org-roam. What if they could Just Work?

Even if you never load org-roam?

Here you go:

(use-package org-node
  :config
  (org-node-db-mode)
  (require 'org-node-shims)
  (mapc #'provide '( org-roam org-roam-id org-roam-db org-roam-node
                     org-roam-utils org-roam-capture org-roam-mode)))

Or this if you develop org-roam extensions and need to build them locally. May also help with other weird issues.

(use-package org-node
  :config
  (org-node-db-mode)
  (require 'org-node-shims-unhygienic))

Misc

Manage org-id-locations

Ever run into “ID not found” situations? Org-node gives you an extra way to feed data to org-id, as I find clumsy the built-in options.

Example setting:

(setq org-node-extra-id-dirs
      '("~/org/"
        "~/Syncthing/"
        "/mnt/stuff/"))

Do a M-x org-node-reset and see if it can find your notes now.

Undoing a Roam hack

If you have org-roam loaded, but no longer update the DB, opening a link can sometimes send you to an outdated file path due to a line in org-roam-id.el that causes org-id to preferentially look up the org-roam DB instead of org-id’s own table!

Either revert that with the following snippet, or if the extension org-node-fakeroam covers your needs, simply delete the DB (normally located at “~/.emacs.d/org-roam.db”).

;; Undo a Roam override
(with-eval-after-load 'org-roam-id
  (org-link-set-parameters
   ;; This was default value at least from Org 9.1 to 9.7+
   "id" :follow #'org-id-open :store #'org-id-store-link-maybe))

Exclude uninteresting nodes

One user had over a thousand project-nodes, but only just began to do a knowledge base, and wished to avoid seeing the project nodes.

This could work by—for example—excluding anything tagged “project” or perhaps anything that has a TODO state. This excludes both:

(setq org-node-filter-fn
      (lambda (node)
        (not
         (or (org-node-get-todo node)
             (member "project" (org-node-get-tags node))
             (assoc "ROAM_EXCLUDE" (org-node-get-properties node))))))

Or you could go with a whitelist approach, to show only nodes from a certain directory we’ll call “my-personal-wiki”:

(setq org-node-filter-fn
      (lambda (node)
        (and (string-search "/my-personal-wiki/" (org-node-get-file node))
             (not (assoc "ROAM_EXCLUDE" (org-node-get-properties node))))))

(NB: if you don’t know what ROAM_EXCLUDE is, feel free to omit that clause)

Limitation: ROAM_EXCLUDE

Let’s say you have a big archive file, fulla IDs, and you want all the nodes within out of sight.

Putting a :ROAM_EXCLUDE: t at the top won’t do it, because unlike in org-roam, child ID nodes of an excluded node are not excluded! The org-node-filter-fn applies its ruleset to each node in isolation.

However, nodes in isolation do still have inherited tags. So you can exploit that, or the outline path or file name.

It works well for me to filter out any file or directory that happens to contain “archive” in the name:

(setq org-node-filter-fn
      (lambda (node)
        (not (string-search "archive" (org-node-get-file node)))))

Or put something like #+filetags: :hide_node: at the top of each file, and set:

(setq org-node-filter-fn
      (lambda (node)
        (not (member "hide_node" (org-node-get-tags node)))))

Org-capture

You may have heard that org-roam has a set of meta-capture templates: the org-roam-capture-templates.

People who understand the magic of capture templates, they may take this in stride. Me, I never felt confident using a second-order abstraction over an already leaky abstraction.

Can we just use vanilla org-capture? That’d be less scary. The answer is yes!

The secret sauce is (function org-node-capture-target):

(setq org-capture-templates
      '(("i" "Capture into ID node"
         plain (function org-node-capture-target) nil
         :empty-lines-after 1)

        ("j" "Jump to ID node"
         plain (function org-node-capture-target) nil
         :jump-to-captured t
         :immediate-finish t)

        ;; Sometimes handy after `org-node-insert-link', to
        ;; make a stub you plan to fill in later, without
        ;; leaving the current buffer for now
        ("s" "Make quick stub ID node"
         plain (function org-node-capture-target) nil
         :immediate-finish t)))

With that done, you can optionally configure the everyday commands org-node-find & org-node-insert-link to outsource to org-capture when they try to create new nodes:

(setq org-node-creation-fn #'org-capture)

That last optional functionality may be confusing if I describe it – better you give it a spin and see if you like.

Completion-at-point

To complete words at point into known node titles:

(org-node-complete-at-point-mode)
(setq org-roam-completion-everywhere nil) ;; Prevent Roam's variant

FAQ: Any analogue to org-roam-node-display-template?

To customize how the nodes look in the minibuffer, configure org-node-affixation-fn:

M-x customize-variable RET org-node-affixation-fn

A related option is org-node-alter-candidates, which lets you match against the annotations as well as the title:

(setq org-node-alter-candidates t)

Grep

If you have Ripgrep installed on the computer, and Consult installed on Emacs, you can use this command to grep across all your Org files at any time.

(keymap-set global-map "M-s M-g" #'org-node-grep)

This can be a power tool for mass edits. Say you want to rename some Org tag :math: to :Math: absolutely everywhere. Then you could follow a procedure such as:

  1. Use org-node-grep and type :math:
  2. Use embark-export (from package Embark)
  3. Use wgrep-change-to-wgrep-mode (from package wgrep)
  4. Do a query-replace (M-%) to replace all :math: with :Math:
  5. Type C-c C-c to apply the changes

Let org-open-at-point detect refs

(For background, consult the docstring of org-node-link-types.)

Say there’s a link to a web URL, and you’ve forgotten you also have a node listing that exact URL in its ROAM_REFS property.

Wouldn’t it be nice if, clicking on that link, you automatically visit that node first instead of being sent to the web? Here you go:

(add-hook 'org-open-at-point-functions
          #'org-node-try-visit-ref-node)

Limitation: TRAMP

Working with files over TRAMP is unsupported for now. Org-node tries to be very fast, often nulling file-name-handler-alist, which TRAMP needs.

The best way to change this is to file an issue to show you care :-)

Limitation: Encryption

Encrypted nodes probably won’t be found. As with TRAMP, file an issue.

Limitation: Unique titles

If two ID-nodes exist with the same title, one of them disappears from minibuffer completions.

That’s just the nature of completion. Much can be said for embracing the uniqueness constraint, and org-node will print messages about collisions.

Anyway… there’s a workaround. Assuming you leave org-node-affixation-fn at its default setting, adding this to initfiles tends to do the trick:

(setq org-node-alter-candidates t)

This lets you match against the node outline path and not only the title, which resolves most conflicts given that the most likely source of conflict is subheadings in disparate files, that happen to be named the same. Some people make this trick part of their workflow.

NB: for users of org-node-complete-at-point-mode, this workaround won’t help those completions. With some luck you’ll rarely insert the wrong link, but it’s worth being aware. (#62)

Limitation: Org-ref

Org-node supports the Org 9.5 @citations, but not fully the aftermarket org-ref &citations that emulate LaTeX look-and-feel, because it would double the time taken by M-x org-node-reset.

What works is bracketed Org-ref v3 citations that start with “cite”, e.g. [[citep:...]], [[citealt:...]], [[citeauthor:...]], since org-node-parser.el is able to pick them up for free.

What doesn’t work is e.g. [[bibentry:...]] since it doesn’t start with “cite”, nor plain citep:... since it is not wrapped in brackets.

If you need more of Org-ref, you have at least two options:

Toolbox

Basic commands:

  • org-node-find
  • org-node-insert-link
  • org-node-insert-transclusion
  • org-node-insert-transclusion-as-subtree
  • org-node-visit-random
  • org-node-refile
  • org-node-context-raise
  • org-node-context-toggle
  • org-node-seq-dispatch
    • Browse node series – see README
  • org-node-extract-subtree
    • A bizarro counterpart to org-roam-extract-subtree. Export the subtree at point into a file-level node, leave a link in the outline parent of where the subtree was, and show the new file as current buffer.
  • org-node-nodeify-entry
    • (Trivial) Give an ID to the subtree at point, and run the hook org-node-creation-hook
  • org-node-insert-heading
    • (Trivial) Shortcut for org-insert-heading + org-node-nodeify-entry
  • org-node-grep
    • (Requires consult) Grep across all known Org files.

Rarer commands:

  • org-node-rewrite-links-ask
    • Look for link descriptions that got out of sync with the corresponding node title, then prompt at each link to update it
  • org-node-rename-file-by-title
    • Auto-rename the file based on the current #+title or first heading
      • Can be run manually or placed on after-save-hook! When run as a hook, it is conservative, doing nothing until you configure org-node-renames-allowed-dirs.
      • Please note that if your filenames have datestamp prefixes, like org-roam’s default behavior of making filenames such as 20240831143302-node_title.org, it is important to get org-node-datestamp-format right or it may clobber a pre-existing datestamp.

        A message is printed about the rename, but it’s easy to miss.

  • org-node-list-dead-links
    • List links where the destination ID could not be found
  • org-node-lint-all-files
    • Can help you fix a broken setup: it runs org-lint on all known files and generates a report of Org syntax problems, for you to correct manually.

      Org-node assumes all files have valid syntax, but many of the reported problems are survivable.

  • org-node-list-reflinks
    • List all links that aren’t id: links. Also includes citations, even though they are technically not links.
  • org-node-list-feedback-arcs
  • org-node-rename-asset-and-rewrite-links
    • Interactively rename an asset such as an image file and try to update all Org links to them. Requires wgrep.
      • NOTE: It prompts you for a certain root directory, and then only looks for links in there, and in sub and sub-subdirectories and so on – but won’t find a link elsewhere.

        Like if you have Org files under /mnt linking to assets in /home, then those links won’t be updated. Or if you choose ~/org/some-subdir as the root directory, then links in ~/org/file.org will not update. So choose ~/org as the root even if you are renaming something in a subdir.

Rarer commands for org-node-backlink-mode:

  • org-node-backlink-mass-update-drawers
  • org-node-backlink-mass-update-props
  • org-node-backlink-mass-delete-drawers
  • org-node-backlink-mass-delete-props
  • org-node-backlink-fix-buffer

Experimental: Node sequences

Do you already know about “daily-notes”? Then get started with a keybinding such as:

(keymap-set global-map "M-s M-s" #'org-node-seq-dispatch)

and configure org-node-seq-defs. See wiki for premade examples.

What are node seqs?

It’s easiest to explain node sequences if we use “daily-notes” (aka “dailies”) as an example.

Org-roam’s idea of a “daily-note” is the same as an org-journal entry: a file/entry where the title is just today’s date.

You don’t need software for that basic idea, only to make it extra convenient to navigate them and jump back and forth in the series.

Thus, fundamentally, any “journal” or “dailies” software are just operating on a sorted series to navigate through. A node sequence. You could have sequences for, let’s say, historical events, Star Trek episodes, your school curriculum…

Appendix

Appendix I: Rosetta stone

API cheatsheet between org-roam and org-node.

Actionorg-roamorg-node
Get ID near point(org-roam-id-at-point)(org-entry-get-with-inheritance "ID")
Get node at point(org-roam-node-at-point)(org-node-at-point)
Prompt user to pick a node(org-roam-node-read)(org-node-read)
Get node by ID(org-node-by-id ID)
Get list of files(org-roam-list-files)(org-node-list-files)
Get backlink objects(org-roam-backlinks-get NODE)(org-node-get-id-links-to NODE)
Get reflink objects(org-roam-reflinks-get NODE)(org-node-get-reflinks-to NODE)
Get title(org-roam-node-title NODE)(org-node-get-title NODE)
Get title of file where NODE is(org-roam-node-file-title NODE)(org-node-get-file-title NODE)
Get title or name of file where NODE is(org-node-get-file-title-or-basename NODE)
Get full path to file where NODE is(org-roam-node-file NODE)(org-node-get-file NODE)
Get ID(org-roam-node-id NODE)(org-node-get-id NODE)
Get tags(org-roam-node-tags NODE)(org-node-get-tags NODE)
Get tags (local only)(org-node-get-tags-local NODE)
Get tags (inherited only)(org-node-get-tags-inherited NODE)
Get outline level(org-roam-node-level NODE)(org-node-get-level NODE)
Get whether this is a subtree(=< 0 (org-roam-node-level NODE))(org-node-is-subtree NODE)
Get char position(org-roam-node-point NODE)(org-node-get-pos NODE)
Get line number(org-node-get-lnum NODE)
Get properties(org-roam-node-properties NODE)(org-node-get-properties NODE)
Get subtree TODO state(org-roam-node-todo NODE)(org-node-get-todo NODE)
Get subtree SCHEDULED(org-roam-node-scheduled NODE)(org-node-get-scheduled NODE)
Get subtree DEADLINE(org-roam-node-deadline NODE)(org-node-get-deadline NODE)
Get subtree priority(org-roam-node-priority NODE)(org-node-get-priority NODE)
Get outline-path(org-roam-node-olp NODE)(org-node-get-olp NODE)
Get ROAM_REFS(org-roam-node-refs NODE)(org-node-get-refs NODE)
Get ROAM_ALIASES(org-roam-node-aliases NODE)(org-node-get-aliases NODE)
Get ROAM_EXCLUDE(assoc "ROAM_EXCLUDE" (org-node-get-properties NODE))
Get nodes in a file(org-node-get-nodes-in-files FILES)
Ensure fresh data(org-roam-db-sync)(org-node-cache-ensure t t)

Appendix II: How to rollback

Instructions to downgrade to an older version, let’s say 1.6.2.

With Quelpa:

(use-package org-node
  :quelpa (org-node :fetcher github :repo "meedstrom/org-node"
                    :branch "v1.6"))

With vc-use-package on Emacs 29:

(use-package org-node
  :vc (:fetcher github :repo "meedstrom/org-node"
       :branch "v1.6"))

With built-in :vc on Emacs 30+ (but note default value of use-package-vc-prefer-newest means you never update, since it is not aware of Git tags):

(use-package org-node
  :vc (:url "https://github.com/meedstrom/org-node"
       :branch "v1.6"))

With Elpaca as follows. Note that recipe changes only take effect after you do M-x elpaca-delete and it re-clones – the idea is that Elpaca users will prefer to do it manually.

(use-package org-node
  :ensure (:fetcher github :repo "meedstrom/org-node"
           :branch "v1.6"))

…Elpaca can also target an exact version tag. Package manager of the future, it is:

(use-package org-node
  :ensure (:fetcher github :repo "meedstrom/org-node"
           :tag "1.6.2"))

With Straight:

(use-package org-node
  :straight (org-node :type git :host github :repo "meedstrom/org-node"
                      :branch "v1.6"))

Appendix III: Random tips

Org-roam shipped the optional (require '=org-roam-export), a patch to fix id: links in HTML export.

Good news, upstream fixed the root of the issue in 5e9953fa0! Update Org to 9.7+, then set

(setq org-html-prefer-user-labels t)

About

A notetaking system like Roam using Emacs Org-mode

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •