UP | HOME

Exocortex

This is my exocortex, a second brain collection of notes on various topics via org-roam. It's mostly a supplement to my Obsidian second brain, for now at least.

org-roam-graph.svg
Figure 1: View Full-Size Graph

Entries

article

codecrafters

poetry

programming

security

tech-infra

Code

The list and graph above are generated dynamically on publish using the following blocks of code:

Copyright (c) 2024 Aaron Bieber

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Graph Generator

(require 'org-roam)
(require 'seq)

(let* ((nodes (org-roam-db-query [:select [id file title] :from nodes]))
       (links (org-roam-db-query [:select [source dest] :from links]))
       (dot-file (make-temp-file "org-roam-graph" nil ".dot"))
       (svg-file (expand-file-name "org-roam-graph.svg"))
       (node-connections (make-hash-table :test 'equal)))

  ;; Count connections for each node
  (dolist (link links)
    (let ((source (car link))
          (dest (cadr link)))
      (puthash source (1+ (gethash source node-connections 0)) node-connections)
      (puthash dest (1+ (gethash dest node-connections 0)) node-connections)))

  (with-temp-file dot-file
    (insert "graph {\n")
    (insert "  graph [overlap=false, splines=true, layout=neato];\n")
    (insert "  node [shape=point, width=0.1];\n")
    (insert "  edge [color=\"#cccccc\"];\n")
    ;; Insert nodes
    (dolist (node nodes)
      (let* ((id (car node))
             (title (caddr node))
             (connections (gethash id node-connections 0))
             (size (+ 0.02 (* 0.02 (min connections 20)))) ; Cap size increase
             (color-intensity (min (/ connections 5.0) 1.0)))
        (insert (format "  \"%s\" [width=%f, color=\"#%02X0000\", tooltip=\"%s\"];\n"
                        id size
                        (round (* color-intensity 255))
                        (replace-regexp-in-string "\"" "\\\"" title)))))
    ;; Insert edges
    (dolist (link links)
      (insert (format "  \"%s\" -- \"%s\";\n" (car link) (cadr link))))
    (insert "}\n"))

  ;; Convert DOT to SVG using system call with neato engine
  (call-process "neato" nil nil nil "-Tsvg" "-o" svg-file dot-file)

  ;; Clean up temporary file
  (delete-file dot-file)

  svg-file)

Link Generator

;; Generate a list of all entries organized by tag
(let ((tag-map (make-hash-table :test 'equal)))
  (dolist (result (org-roam-db-query
                   "SELECT nodes.title, nodes.id, tags.tag
                      FROM nodes
                      LEFT JOIN tags ON nodes.id = tags.node_id
                      WHERE nodes.level = 0
                        AND nodes.file NOT LIKE '%%/daily/%%'
                        AND nodes.file NOT LIKE '%%index%%'
                        AND nodes.file NOT LIKE '%%/j/%%'
                      ORDER BY tags.tag, nodes.title"))
    (let* ((title (nth 0 result))
           (id (nth 1 result))
           (tag (or (nth 2 result) "Untagged"))
           (file (org-roam-node-file (org-roam-node-from-id id)))
           (rel-file (file-relative-name file (file-name-directory buffer-file-name)))
           (html-file (concat (file-name-sans-extension rel-file) ".html"))
           (entry (format "  - [[file:%s][%s]]" html-file title)))
      (push entry (gethash tag tag-map))))

  (maphash (lambda (tag entries)
             (princ (format "** %s\n%s\n"
                            tag
                            (mapconcat #'identity (nreverse entries) "\n"))))
           tag-map))