Recreating the Cortex Theme Journal in Emacs Org Mode

It’s the New Years season again, and that means resolutions. Usually. This time around, I’m trying something a little new. Inspired by CGP Grey’s video on the topic, I’m trying “theme system journaling.” But since I’m not very good at keeping physical journals, I’ve recapitulated the system in emacs using org-mode, yasnippet, and some additional code. Here, I’ll go into detail on what I did and how it’s working so far. I learned some really neat org-mode features I wasn’t aware of; this has been a fun journey!

Before I go on: If you’re not familiar with the Theme System Journal, check out the video above. CGP Grey made clear in the video describing how he uses the journal that he doesn’t care if people recapitulate it, but if you want to toss a thank-you his way, he does have a Patreon. And this post is not affiliated with him or the Theme System Journal.

Marge Simpson holding the CGP Grey logo and saying 'I just think they're neat!

Thanks, Marge.

Journal consists of three pieces:

  • A theme page for the year (or season)
  • A daily habit tracker
  • A daily journal

The Theme

This part is the most straightforward: I put up one page describing the theme. On this page, I have links out to my habit tracker and the daily journal (org-mode makes it easy to do links; [[relative-path/to-file.org][title]] is conidered a link and C-c C-o will navigate to the link target). Under the theme, I have a short explanation of why I chose this theme (useful to keep that in focus because if your causes change, the theme may change), some possible accomplishments that would satisfy the theme (not goals: key to the process is avoiding prognosticating specific goals that you can fail at, but instead deciding on things you can aim towards), and some key ideas to justify the theme.

My theme for 2024 is (not surprisingly, given the topic of this post) organization. I’m tired of tripping over things in the house. I’d like to organize the basement, keep sitting surfaces in the house clear, and let go of things I don’t use. And osme key ideas are that things not in boxes (i.e. visible on a shelf) are easier to find and use and if it’s in a box the box should be labeled. So that’s started.

Habits

The Theme System Journal provides pages and pages of daily habit trackers. This just screams “table,” and org-mode loves tables.

Weekly habits, showing the insert 1 week button and a week of habits.

You can ignore the :ID: at the top; I use org-roam and the ID lets it find this topic in its database for direct navigation.

The Insert 1 week “link” takes advantage of a feature in org-mode: any link can be preceeded by elisp: and the text will be interpreted as a lisp expression and evaluated. The full text of that link is [[(org-journaling-habit-go-to-line-insert-1-week)][Insert 1 week]]

!
This, of course, is a major security issue, so emacs will warn you by default before you execute an expression thinking you’re chasing a link; you can disable this behavior with (setq org-confirm-elisp-link-function nil). Use that at your own risk!

That link is invoking a function I’ve written to toss a week of content onto that Habit table.

;; Copyright 2024 Mark T. Tomczak; code released under MIT license
(defun org-journaling-habit-insert-next-day ()
  "Insert next day column at end.  Run this from top of habit column."
  (interactive)
  (end-of-line)
  (backward-char 2)
  (search-backward "|")
  (forward-char 2)
  (let* ((beginning (point))
	 (end (search-forward ">"))
	 (date (buffer-substring beginning end)))
    (end-of-line)
    (insert " " date " |")
    (backward-char 2)
    (org-timestamp-up-day)
    (end-of-line)
    (forward-line)
    (insert "|")
    (forward-line)
    (insert "|")
    (backward-char 1)))

(defun org-journaling-habit-go-to-line-insert-1-week ()
  "Insert a week of habits.
This is intended to be called from a link in `org-mode'."
  (search-forward "| Habit")
  (dotimes (x 7)
    (org-journaling-habit-insert-next-day)
    (forward-line -2))
  (org-cycle))

The rest is pretty low-tech: I put a ‘(’ in a column if I made a move towards the habit and a ‘()’ if I accomplished the habit that day.

Daily Journal

The daily journal view, showing one day of data, and the “Insert next day” button

Daily journal follows CGP Grey’s preferred method (he notes this isn’t one-size-fits-all, but it’s working for me right now). Every day, I note where I am, one thing I’m personally and professionally grateful for, and one goal for the day.

There are many ways to insert these, but I prefer to use a yasnippet template, which is below. This lets me navigate to each section of the journal entry with <tab> as I’m writing it, which I love.

# -*- mode: snippet -*-
# name: Daily journaling
# key: daily-journaling
# copyright 2024 Mark T. Tomczak, code released under the MIT license
# --

** `(format-time-string "<%Y-%m-%d %a>" (current-time))`

** Where am I?
$1

** I am personally grateful for
$2

** I am professionally grateful for
$3

** Today's thoughts
$4

** Today's goal
*** TODO $5

Only tricky parts of using this is that yasnippet doesn’t always play well with org-mode and it has some sharp edges around being programmatically driven. I solve the first problem with the -*- eval: (yas-global-mode) -*- stanza at the top to force yasnippet to recompute its snippets when the file opens. The second issue has a solution recommended by the author of yasnippet themselves, which I’ve recapitulated below.

; source: https://stackoverflow.com/questions/10211730/insert-yasnippet-by-name
(defun yas/insert-by-name (name)
  (flet ((dummy-prompt
          (prompt choices &optional display-fn)
          (declare (ignore prompt))
          (or (find name choices :key display-fn :test #'string=)
              (throw 'notfound nil))))
    (let ((yas/prompt-functions '(dummy-prompt)))
      (catch 'notfound
        (yas/insert-snippet t)))))

Using that, I have a function triggered by the Insert next day link:

;; Copyright 2024 Mark T. Tomczak; code released under MIT license
(defun org-journaling-daily-insert-day ()
  "Insert an entry for today two lines under point."
  (forward-line 2)
  (yas/insert-by-name "Daily journaling"))

… and that’s it! I’ve only been using this approach for a couple days, but it’s making me pretty happy to play with it. Now to see if it actually works for me.

Comments