Sharing Org Roam content

Four ways I share my notes from Org Roam

Index

  1. Introduction
  2. Sharing restricted content
  3. Sharing open content
  4. Sharing synchronously with multiple people
  5. Creating and sharing to the world

Introduction

I once was bothered by how isolated I felt in org-roam. I felt I should have ways to build collaborative docs or send a link to people so they could consume the content I write. I was highly inspired by Jethro's braindump and Andy Matuschak's working notes. At some point, I realized my ambitions were different and lower than those involved in building and researching note-taking. Beyond that, all my collaboration today happens via my full-time job using Google Docs, Miro, and Confluence. I might sketch things in org-roam and use it as a thinking environment while I'm collaborating via those other tools.

However, after more than four years of using it, something stood out: the desire to share my thinking process or atomic notes to help others with the problem at hand.

I will share my day-to-day use cases, how I select and remix my Org roam nodes, different mediums I use (PDF, online, and presentations), and some code so others can use similar workflows if they fit.

Sharing restricted content

Sometimes, in a one-on-one or small group meeting at work, someone asks me for advice about a problem. I usually answer live using my top-of-mind references and experiences, but I promise to send further details and additional resources.

Since I might insert the specific context of my company and team, or even the selection of the topics can reveal what we are working on, which might be low-risk but nonetheless unnecessary, I prefer to generate a PDF and share it directly with the person or the group.

To discover and select the material, I use both the semantic search and the Q&A functionalities. Even if I couldn't recall it live, it will help refresh me on that topic. It isn't a necessary step; I imagine many can just select based on their current knowledge about their notes and references.

The material might be a single org roam file, a node, or a collection of nodes spread in different files.

Two straightforward options are sharing the entire org file, which might contain many org roam nodes, or selecting only one node for it. One can use the org-latex-export-to-pdf, which I modified slightly in the following function so I don't have to pick the file in the roam folder but in a folder with fewer files that gets cleaned from time to time—in my case, the Downloads folder.

(defun lgm/export-org-to-downloads-folder ()
  "Export the current Org file to PDF and save it in the downloads folder.
Prompts for the output filename, defaulting to the original Org file's name."
  (interactive)
  (let* ((org-file (buffer-file-name))
         (downloads-folder "~/Downloads/") ; Change this path to your desired folder
         (default-pdf-name (concat (file-name-sans-extension (file-name-nondirectory org-file)) ".pdf"))
         (output-file (read-file-name "Save PDF as: "
                                      downloads-folder
                                      (concat downloads-folder default-pdf-name)
                                      nil
                                      default-pdf-name)))

    ;; Ensure the buffer is visiting a valid file
    (unless (and org-file (file-exists-p org-file))
      (error "Buffer is not visiting a file or file does not exist"))

    ;; Export the Org file to PDF
    (org-latex-export-to-pdf)

    ;; Move the exported PDF to the specified location
    (let ((exported-pdf (concat (file-name-sans-extension org-file) ".pdf")))
      (if (file-exists-p exported-pdf)
          (rename-file exported-pdf output-file t)
        (error "PDF file not found after export")))

    (message "Org file exported to PDF and saved as %s" output-file)))

However, you likely want to share a collection of org roam nodes that mix different references. That's the power of the zettelkasten method. An essential emacs package for it is the org-transclusion. It lets you import notes content into other org roam files. Let me use an example. Once asked about interviewing questions for platforms' internal customers, I had a few org roam nodes I wanted to share.

I can start by creating a new heading in the file where I'm taking meeting notes. I can then start collecting the nodes returned by the semantic search and the Q&A while inserting specific context about the problem.

After that, I select the Org sub-tree with C-x n s (you can revert by using C-x n w), and I call the same previous function to export it. Here's how the narrowed Org roam file looks like without adding the transclusion:

The org-transclusion let me quickly grab pieces of my personal knowledge base and compose something personalized for the situation.

The generated pdf looks fine for a quick share.

A couple of times, I preferred to generate ASCII text. When I make career conversations, I have the questions written in the Org roam file about that person. Still, since I want them to reflect on it a couple of days before, I would export as ASCII using 'org-ascii-export-as-ascii' and copy and paste in a direct message to them.

Sharing open content

When I want to share notes on bibliographical references, like a book or article in which I don't make any connection to information that shouldn't be public, I share it online, but in an extremely ad-hoc way, very differently from full-feature digital gardens. Here's example 1 and example 2 1. I list them in a page so I can easily ctrl+f to share the links.

This is the online version of one of my Org roam files. I use a function to make pushing a single file online very convenient.

The following code is very specific for my use case but can be a starting point for others.

;; It is fixed for my personal blog case, so I dont have to add it to the org-roam files
(setq org-html-head-extra
      (concat "<link rel='stylesheet' type='text/css' href='../org-roam/org.css' />"))

;; Publishing org-roam files to my personal website so I can easily share notes
(defun lgm/publish-org-roam-to-github (github-repo-dir output-dir branch)
  "Publish the current Org Roam file to a GitHub Pages site, including images.

GITHUB-REPO-DIR: Path to your GitHub Pages repository.
OUTPUT-DIR: Directory inside the repository where the HTML file will be placed.
BRANCH: Branch to which changes should be committed (e.g., 'gh-pages')."
  (interactive
   (list (read-directory-name "GitHub Repo Directory: " "~/repos/lgmoneda.github.io/")
         (read-string "Output Directory (relative to repo root): " "org-roam/")
         (read-string "Branch: " "master")))
  (let* ((org-file (buffer-file-name))
         (org-html-export-file (concat (file-name-sans-extension org-file) ".html"))
         (output-path (expand-file-name output-dir github-repo-dir))
         (output-file (expand-file-name (file-name-nondirectory org-html-export-file) output-path))
         (image-dir (expand-file-name "images/org-roam/" github-repo-dir))
         (image-paths (make-hash-table :test 'equal)))
    (unless org-file
      (error "This buffer is not visiting a file"))
    (unless (derived-mode-p 'org-mode)
      (error "This function only works in Org mode buffers"))

    ;; Sync org-roam db
    (org-roam-db-sync)

    ;; Ensure the image directory exists
    (unless (file-directory-p image-dir)
      (make-directory image-dir t))

    ;; Find and copy images referenced in the Org file
    (org-element-map (org-element-parse-buffer) 'link
      (lambda (link)
        (when (string= (org-element-property :type link) "file")
          (let* ((image-path (org-element-property :path link))
                 (absolute-image-path (expand-file-name image-path (file-name-directory org-file)))
                 (relative-image-path (file-relative-name absolute-image-path (file-name-directory org-file)))
                 (image-filename (file-name-nondirectory image-path))
                 (destination-image-path (expand-file-name image-filename image-dir)))
            (when (and image-path (file-exists-p absolute-image-path))
              ;; Copy the image and store the mapping for replacement
              (copy-file absolute-image-path destination-image-path t)
              (puthash relative-image-path
                       (concat "../images/org-roam/" image-filename)
                       image-paths))))))

    (message "Image Paths: %s"
             (mapcar (lambda (key)
                       (list key (gethash key image-paths)))
                     (hash-table-keys image-paths)))

    ;; Export the Org file to HTML
    (org-html-export-to-html)

    ;; Ensure the output directory exists
    (unless (file-directory-p output-path)
      (make-directory output-path t))

    ;; Move the exported HTML to the output directory
    (copy-file org-html-export-file output-file t)

    ;; Post-process the HTML file to update image paths
    (with-temp-buffer
      (insert-file-contents output-file)
      (dolist (key (hash-table-keys image-paths))
        (goto-char (point-min))
        (while (search-forward key nil t)
          (replace-match (gethash key image-paths) t t)))
      (write-file output-file))

    (message "Exported %s to %s" org-file output-file)

    ;; Commit and push the changes to GitHub
    (let ((default-directory github-repo-dir))
      (shell-command (format "git add %s" (shell-quote-argument output-file)))
      (shell-command (format "git add %s"
                             (shell-quote-argument
                              (expand-file-name "images/org-roam/" github-repo-dir))))
      (shell-command
       (format "git commit --no-gpg-sign -m 'Publish %s with images'"
               (file-name-nondirectory output-file)))
      (shell-command (format "git push origin %s" branch))
      (message "Published to GitHub Pages branch: %s" branch))))

Sharing synchronously with multiple people

Once, I got very excited about a book that I wanted to share with my team. Generating notes from everything I wrote in the bibliographical notes would generate a very long PDF, and I also wanted to do a live activity using my team's context to practice the concepts. In this scenario, I created a new Org roam file and mixed new content with Org roam nodes to prepare a slide presentation.

Another awesome usage of org-transclusion is preparing a slide presentation

I use the org-present package, and most of my custom configuration was inspired by this material by System Crafters.

Org-present makes it convenient to quickly put together presentations when combined with org-transclusion

Since we don't take notes about the slide format, reusing Org roam nodes generated a wordy presentation. It was a lesson in isolating concepts better. At the same time, I prepared a couple of presentations extremely quickly.

The second scenario I preferred was when I wanted to do a company-wide presentation on a generic topic for a knowledge-sharing event. I prepared it entirely in Org roam since I wanted to keep the generated assets for future reference and reuse. It indeed happened, and it became an article in my Data Science Leadership material.

Creating and sharing to the world

When blogging, I write the post in Org Roam first since I want to link to nodes and explore my graph. As a result, I had to write it twice and translate it to Markdown when pushing it to this blog. Though the overhead wasn't that meaningful, I also realized I was refraining from adding org roam links to the text only to reduce the work of excluding them when manually exporting it to Markdown.

After adopting some syntax in Org to match what I used to generate in the blog by using markdown, I could leverage pandoc export to HTML to write it in a single place and export it to my blog. This is the first post I'm doing using it, and it feels great. Every post I've seen about publishing a blog using Org roam uses ox-hugo, so it seems more straightforward if you don't use Jekyll already; examples 1 and 2.

The Jekyll yaml front matter lies inside a drawer at the top of the Org roam file, so most of the configuration isn't visible.

My code for it isn't very generic, and the pandoc arguments try to reproduce what I was getting before when using only markdown. For example, starting my headings with h2. Regardless, it is here if you want to take a look for inspiration.


  1. I started doing it recently, and I still don't know what to do with Org roam links. In the code provided, they are all broken links. For the blog post case, I exclude all of them.↩︎