Categories: geek » emacs » org

View topic page - RSS - Atom - Subscribe via email

Treemap visualization of an Org Mode file

| org, visualization

[2025-01-12 Sun]: u/dr-timeous posted a treemap_org.py · GitHub that makes a coloured treemap that displays the body on hover. (Reddit) Also, I think librsvg doesn't support wrapped text, so that might mean manually wrapping if I want to figure out the kind of text density that webtreemap has.

One of the challenges with digital notes is that it's hard to get a sense of volume, of mass, of accumulation. Especially with Org Mode, everything gets folded away so neatly and I can jump around so readily with C-c j (org-goto) or C-u C-c C-w (org-refile) that I often don't stumble across the sorts of things I might encounter in a physical notebook.

Treemaps are a quick way to visualize hierarchical data using nested rectangles or squares, giving a sense of relative sizes. I was curious about what my main organizer.org file would look like as a treemap, so I wrote some code to transform it into the kind of data that https://github.com/danvk/webtreemap wants as input. webtreemap creates an HTML file that uses Javascript to let me click on nodes to navigate within them.

For this treemap prototype, I used org-map-entries to go over all the headings and make a report with the outline path and the size of the heading. To keep the tree visualization manageable, I excluded done/cancelled tasks and archived headings. I also wanted to exclude some headings from the visualization, like the way my Parenting subheading has lots of personal information underneath it. I added a :notree: tag to indicate that a tree should not be included.

Screencast of exploring a treemap

Reflections

2025-01-11_19-54-47.png
Figure 1: Screenshot of the treemap for my organizer.org

The video and the screenshot above show the treemap for my main Org Mode file, organizer.org. I feel like the treemap makes it easier to see projects and clusters where I'd accumulated notes, both in terms of length and quantity. (I've omitted some trees like "Parenting" which take up a fairly large chunk of space.)

To no one's surprise, Emacs takes up a large part of my notes and ideas. =)

When I look at this treemap, I notice a bunch of nodes I need to mark as DONE or CANCELLED because I forgot to update my organizer.org. That usually happens when I come up with an idea, don't remember that I'd come up with it before, put it in my inbox.org file, and do it from there or from the organizer.org location I've refiled it to without bumping into the first idea. Once in a blue moon, I go through my whole organizer.org file and clean out the cruft. Maybe a treemap like this will make it easier to quickly scan things.

Interestingly, "Explore AI" takes up a disproportionately large chunk of my "Inactive Projects" visualization, even though I spend more time and attention on other things. Large language models make it easy to generate a lot of text, but I haven't really done the work to process those. I've also collected a lot of links that I haven't done much with.

It might be neat to filter the headings by timestamp so that I can see things I've touched in the last 6 months.

Hmm, looking at this treemap reminds me that I've got "organizer.org/Areas/Ideas for things to do with focused time/Writing/", which probably should get moved to the posts.org file that I tend to use for drafts. Let's take look at the treemap for that file. (Updated: cleared it out!)

2025-01-11_20-10-18.png
Figure 2: Drafts in my posts.org

Unlike my organizer.org file, my posts.org file tends to be fairly flat in terms of hierarchy. It's just a staging ground for ideas before I put them on my blog. I usually try to keep posts short, but a few of my posts have sub-headings. Since the treemap makes it easy to see nodes that are larger or more complex, that could be a good nudge to focus on getting those out the door. Looking at this treemap reminds me that I've got a bunch of EmacsConf posts that I want to finish so that I can document more of our processes and tools.

2025-01-11_14-52-28.png
Figure 3: Treemap of my inbox

My inbox.org is pretty flat too, since it's really just captured top-level notes that I'll either mark as done or move somewhere else (usually organizer.org). Because the treemap visualization tool uses / as a path separator, the treemap groups headings that are plain URLs together, grouped by domain and path.

2025-01-12_08-30-44.png
Figure 4: Treemap of my Emacs configuration

My Emacs configuration is organized as a hierarchy. I usually embed the explanatory blog posts in it, which explains the larger nodes. I like how the treemap makes it easy to see the major components of my configuration and where I might have a lot of notes/custom code. For example, my config has a surprising amount to do with multimedia considering Emacs is a text editor, and that's mostly because I like to tinker with my workflow for sketchnotes and subtitles. This treemap would be interesting to colour based on whether something has been described in a blog post, and it would be great to link the nodes in a published SVG to the blog post URLs. That way, I can more easily spot things that might be fun to write about.

The code

This assumes https://github.com/danvk/webtreemap is installed with npm install -g webtreemap-cli.

(defvar my-org-treemap-temp-file "~/treemap.html") ; Firefox inside Snap can't access /tmp
(defvar my-org-treemap-command "treemap" "Executable to generate a treemap.")

(defun my-org-treemap-include-p (node)
  (not (or (eq (org-element-property :todo-type node) 'done)
           (member "notree" (org-element-property :tags node))
           (org-element-property-inherited :archivedp node 'with-self))))

(defun my-org-treemap-data (node &optional path)
  "Output the size of headings underneath this one."
  (let ((sub
         (apply
          'append
          (org-element-map
              (org-element-contents node)
              '(headline)
            (lambda (child)
              (if (my-org-treemap-include-p child)
                  (my-org-treemap-data
                   child
                   (append path
                           (list
                            (org-no-properties
                             (org-element-property :raw-value node)))))
                (list
                 (list
                  (-
                   (org-element-end child)
                   (org-element-begin child))
                  (string-join
                   (cdr
                    (append path
                            (list
                             (org-no-properties
                              (org-element-property :raw-value node))
                             (org-no-properties
                              (org-element-property :raw-value child)))))
                   "/")
                  nil))))
            nil nil 'headline))))
    (append
     (list
      (list
       (-
        (org-element-end node)
        (org-element-begin node)
        (apply '+ (mapcar 'car sub))
        )
       (string-join
        (cdr
         (append path
                 (list
                  (org-no-properties (org-element-property :raw-value node)))))
        "/")
       (my-org-treemap-include-p node)))
     sub)))

(defun my-org-treemap ()
  "Generate a treemap."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((file (expand-file-name (expand-file-name my-org-treemap-temp-file)))
          (data (cdr (my-org-treemap-data (org-element-parse-buffer)))))
      (with-temp-file file
        (call-process-region
         (mapconcat
          (lambda (entry)
            (if (elt entry 2)
                (format "%d %s\n" (car entry)
                        (replace-regexp-in-string org-link-bracket-re "\\2" (cadr entry)))
              ""))
          data
          "")
         nil
         my-org-treemap-command nil t t))
      (browse-url (concat "file://" (expand-file-name my-org-treemap-temp-file))))))

There's another treemap visualization tool that can produce squarified treemaps as coloured SVGs, so that style might be interesting to explore too.

Next steps

I think there's some value in being able to look at and think about my outline headings with a sense of scale. I can imagine a command that shows the treemap for the current subtree and allows people to click on a node to jump to it (or maybe shift-click to mark something for bulk action), or one that shows subtrees summing up :EFFORT: estimates or maybe clock times from the logbook, or one limited by a timestamp range, or one that highlights matching entries as you type in a query, or one that visualizes s-exps or JSON or project files or test coverage.

It would probably be more helpful if the treemap were in Emacs itself, so I could quickly jump to the Org nodes and read more or mark something as done when I notice it. boxy-headings uses text to show the spatial relationships of nested headings, which is neat but probably not up to handling this kind of information density. Emacs can also display SVG images in a buffer, animate them, and handle mouse-clicks, so it could be interesting to implement a general treemap visualization which could then be used for all sorts of things like disk space usage, files in project modules, etc. SVGs would probably be a better fit for this because that allows increased text density and more layout flexibility.

It would be useful to browse the treemap within Emacs, export it as an SVG so that I can include it in a webpage or blog post, and add some Javascript for web-based navigation.

The Emacs community being what it is (which is awesome!), I wouldn't be surprised if someone's already figured it out. Since a quick search for treemap in the package archives and various places doesn't seem to turn anything up, I thought I'd share these quick experiments in case they resonate with other people. I guess I (or someone) could figure out the squarified treemapping algorithm or the ordered treemap algorithm in Emacs Lisp, and then we can see what we can do with it.

I've also thought about other visualizations that can help me see my Org files a different way. Network graphs are pretty popular among the org-roam crew because org-roam-ui makes them. Aside from a few process checklists that link to headings that go into step-by-step detail and things that are meant to graph connections between concepts, most of my Org Mode notes don't intentionally link to other Org Mode notes. (There are also a bunch of random org-capture context annotations I haven't bothered removing.) I tend to link to my public blog posts, sketches, and source code rather than to other headings, so that's a layer of indirection that I'd have to custom-code. Treemaps might be a good start, though, as they take advantage of the built-in hierarchy. Hmm…

View org source for this post

Org Babel, Mermaid JS, and fixing "Failed to launch the browser process" on Ubuntu 24

| emacs, org

Mermaid makes pretty diagrams from text. It's Javascript-based, so the command-line tool (mmdc) uses Puppeteer to get the results of evaluating the diagram in the browser. I was running into some errors trying to get it to work from Org Mode over ob-mermaid on Ubuntu 24, since apparently AppArmor restricts Puppeteer. (Error: Failed to launch the browser process! · Issue #730 · mermaid-js/mermaid-cli).

I put together a pull request to modify ob-mermaid-cli-path so that it doesn't get quoted and can therefore have the aa-exec command needed to work around that. With that modified org-babel-execute:mermaid, I can then configure ob-mermaid like this:

(use-package ob-mermaid
  :load-path "~/vendor/ob-mermaid")
;; I need to override this so that the executable isn't quoted
(setq ob-mermaid-cli-path "aa-exec --profile chrome mmdc -c ~/.config/mermaid/config.json")

I also ran into a problem where the library that Emacs uses to display SVGs could not handle the foreignObject elements used for the labels. mermaid missing text in svg · Issue #112 · mermaid-js/mermaid-cli . Using the following ~/.config/mermaid/config.json fixed it, and I put the option in the ob-mermaid-cli-path above so that it always gets loaded.

{
  "flowchart": {
    "useMaxWidth": false,
    "htmlLabels": false
  }
}

Here's sample Mermaid markup and the file it creates:

mindmap
  root((test))
    Node 1
      Node 1A
      Node 1B
    Node 2
    Node 3
testNode 1Node 2Node 3Node 1ANode 1B

Now I can see the labeled diagrams inside Emacs, too.

This is part of my Emacs configuration.
View org source for this post

Using image-dired to browse the latest screenshots from multiple directories

Posted: - Modified: | emacs, image, org, link

[2025-01-06 Mon]: Patch in progress, Stefan Kangas is looking into it.

Since A+ and I play lots of Minecraft together, I figured it's a good opportunity to slowly get her into the idea of documenting learning. Besides, I can always practise it myself. Screenshots are handy for that. In Minecraft Java, F1 hides the usual heads-up display, and F2 takes the screenshot. Usually, when I start taking screenshots. A+ starts taking screenshots too. I want to build on her enthusiasm by including her screenshots in notes. To make it easy to incorporate her pictures into our notes, I've shared her GDLauncher folder and her Videos folder with my computer using Syncthing so that I can grab any screenshots or videos that she's taken.

In Emacs, image-dired makes it easy to see thumbnails. The neat thing is that it doesn't just work with a single directory. Just like Dired, you can give it a cons cell with a directory in the first part and a list of files in the second part as the first argument to the function, and it will display those files. This means I can use directory-files-recursively to make a list of files, sort it to show most recent screenshots first, limit it to the most recent items, and then display a buffer with thumbnails. image-dired-show-all-from-dir reports a small error when you do this (I need to send a patch upstream), so we hush it with condition-case in my-show-combined-screenshots.

(defvar my-screenshot-dirs
  '("~/recordings"
    "~/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instances/"
    "~/sync/gdlauncher-instances/"))
(defvar my-recent-screenshot-limit 50)

(defun my-combined-screenshots (&optional limit)
  (seq-take
   (sort
    (seq-mapcat (lambda (dir)
                  (directory-files-recursively dir "^[0-9][0-9][0-9][0-9]-.*\\.\\(png\\|webm\\)"))
                my-screenshot-dirs)
    :key #'file-name-base
    :lessp #'string<
    :reverse t)
   (or limit my-recent-screenshot-limit)))

(defun my-latest-screenshot ()
  (car (my-combined-screenshots)))

(defun my-show-combined-screenshots (&optional limit)
  "Show thumbnails for combined screenshots."
  (interactive (list (when current-prefix-arg (read-number "Limit: "))))
  (condition-case nil
      ;; ignore errors from image-dired trying to set default-directory
      (image-dired-show-all-from-dir
       (cons (car my-screenshot-dirs) (my-combined-screenshots limit)))
    (error nil)))
2025-01-02_08-19-32.png
Figure 1: The result of my-show-combined-screenshots

In the *image-dired* buffer created by my-show-combined-screenshots, I can use m (image-dired-mark-thumb-original-file) to mark images and C-u w (image-dired-copy-filename-as-kill) to copy their absolute paths.

To make it easier to create links to a file by using org-store-link (which I've bound to C-c l) and org-insert-link (C-c C-l in an Org buffer), I can define a link-storing function that takes the original filename:

(defun my-org-image-dired-store-link ()
  (when (and (derived-mode-p 'image-dired-thumbnail-mode)
             (get-text-property (point) 'original-file-name))
    (org-link-store-props
     :link (concat "file:" (get-text-property (point) 'original-file-name)))))

(with-eval-after-load 'org
  (org-link-set-parameters
   "image-dired"
   :store #'my-org-image-dired-store-link))

To make it easier to insert the marked links that have been copied as absolute paths:

(defun my-org-yank-file-links-from-kill-ring ()
  (interactive)
  (dolist (file (read (concat "(" (current-kill 0) ")")))
      (insert (org-link-make-string (concat "file:" file)) "\n")))

I usually want to copy those files to another directory anyway. I have a my-org-copy-linked-files function in Copy linked file and change link that copies the files and rewrites the Org links. This means that I can copy my notes to an index.org in a directory I share with A+, save the images to an images subdirectory, and export the index.html so that she can read the notes any time she likes.

This is part of my Emacs configuration.
View org source for this post

Linking to Org Babel source in a comment, and making that always use file links

| emacs, org

I've been experimenting with these default header args for Org Babel source blocks.

(setq org-babel-default-header-args
      '((:session . "none")
        (:results . "drawer replace")
        (:comments . "link")  ;; add a link to the original source
        (:exports . "both")
        (:cache . "no")
        (:eval . "never-export") ;; explicitly evaluate blocks instead of evaluating them during export
        (:hlines . "no")
        (:tangle . "no"))) ;; I have to explicitly set up blocks for tangling

In particular, :comments link adds a comment before each source block with a link to the file it came from. This allows me to quickly jump to the actual definition. It also lets me use org-babel-detangle to copy changes back to my Org file.

I also have a custom link type to make it easier to link to sections of my configuration file (Links to my config). Org Mode prompts for the link type to use when more than one function returns a link for storing, so that was interrupting my tangling with lots of interactive prompts. The following piece of advice ignores all the custom link types when tangling the link reference. That way, the link reference always uses the file: link instead of offering my custom link types.

(advice-add #'org-babel-tangle--unbracketed-link
            :around (lambda (old-fun &rest args)
                      (let (org-link-parameters)
                        (apply old-fun args))))
This is part of my Emacs configuration.
View org source for this post

Updating my audio braindump workflow to take advantage of WhisperX

| emacs, speechtotext, org

I get word timestamps for free when I transcribe with WhisperX, so I can skip the Aeneas alignment step. That means I can update my previous code for handling audio braindumps . Breaking the transcript up into sections Also, I recently updated subed-word-data to colour words based on their transcription score, which draws my attention to things that might be uncertain.

Here's what it looks like when I have the post, the transcript, and the annotated PDF.

2024-11-17_20-44-30.png
Figure 1: Screenshot of draft, transcript, and PDF

Here's what I needed to implement my-audio-braindump-from-whisperx-json (plus some code from my previous audio braindump workflow):

(defun my-whisperx-word-list (file)
  (let* ((json-object-type 'alist)
         (json-array-type 'list))
    (seq-mapcat (lambda (seg)
                  (alist-get 'words seg))
                (alist-get 'segments (json-read-file file)))))

;; (seq-take (my-whisperx-word-list (my-latest-file "~/sync/recordings" "\\.json")) 10)
(defun my-whisperx-insert-word-list (words)
  "Inserts WORDS with text properties."
  (require 'subed-word-data)
  (mapc (lambda (word)
            (let ((start (point)))
              (insert
               (alist-get 'word word))
              (subed-word-data--add-word-properties start (point) word)
              (insert " ")))
        words))

(defun my-audio-braindump-turn-sections-into-headings ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "START SECTION \\(.+?\\) STOP SECTION" nil t)
    (replace-match
     (save-match-data
       (format
        "\n*** %s\n"
        (save-match-data (string-trim (replace-regexp-in-string "^[,\\.]\\|[,\\.]$" "" (match-string 1))))))
     nil t)
    (let ((prop-match (save-excursion (text-property-search-forward 'subed-word-data-start))))
      (when prop-match
        (org-entry-put (point) "START" (format-seconds "%02h:%02m:%02s" (prop-match-value prop-match)))))))

(defun my-audio-braindump-split-sentences ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "[a-z]\\. " nil t)
    (replace-match (concat (string-trim (match-string 0)) "\n") )))

(defun my-audio-braindump-restructure ()
  (interactive)
  (goto-char (point-min))
  (my-subed-fix-common-errors)
  (org-mode)
  (my-audio-braindump-prepare-alignment-breaks)
  (my-audio-braindump-turn-sections-into-headings)
  (my-audio-braindump-split-sentences)
  (goto-char (point-min))
  (my-remove-filler-words-at-start))

(defun my-audio-braindump-from-whisperx-json (file)
  (interactive (list (read-file-name "JSON: " "~/sync/recordings/" nil nil nil (lambda (f) (string-match "\\.json\\'" f)))))
  ;; put them all into a buffer
  (with-current-buffer (get-buffer-create "*Words*")
    (erase-buffer)
    (fundamental-mode)
    (my-whisperx-insert-word-list (my-whisperx-word-list file))
    (my-audio-braindump-restructure)
    (goto-char (point-min))
    (switch-to-buffer (current-buffer))))

(defun my-audio-braindump-process-text (file)
  (interactive (list (read-file-name "Text: " "~/sync/recordings/" nil nil nil (lambda (f) (string-match "\\.txt\\'" f)))))
  (with-current-buffer (find-file-noselect file)
    (my-audio-braindump-restructure)
    (save-buffer)))
;; (my-audio-braindump-from-whisperx-json (my-latest-file "~/sync/recordings" "\\.json"))

Ideas for next steps:

  • I can change my processing script to split up the Whisper TXT into sections and automatically make the PDF with nice sections.
  • I can add reminders and other callouts. I can style them, and I can copy reminders into a different section for easier processing.
  • I can look into extracting PDF annotations so that I can jump to the next highlight or copy highlighted text.
This is part of my Emacs configuration.
View org source for this post

Changing Org Mode underlines to the HTML mark element

| org

Apparently, HTML has a mark element that is useful for highlighting. ox-html.el in Org Mode doesn't seem to export that yet. I don't use _ to underline things because I don't want that confused with links. Maybe I can override org-html-text-markup-alist to use it for my own purposes…

(with-eval-after-load 'org
  (setf (alist-get 'underline org-html-text-markup-alist)
        "<mark>%s</mark>"))

Okay, let's try it with:

Let's see _how that works._

Let's see how that works. Oooh, that's promising.

Now, what if I want something fancier, like the way it can be nice to use different-coloured highlighters when marking up notes in order to make certain things jump out easily? A custom link might come in handy.

(defun my-org-highlight-export (link desc format _)
  (pcase format
    ((or '11ty 'html)
     (format "<mark%s>%s</mark>"
             (if link
                 (format " class=\"%s\"" link)
               link)
             desc))))
(with-eval-after-load 'org
  (org-link-set-parameters "hl" :export 'my-org-highlight-export)
  )

A green highlight might be good for ideas, while red might be good for warnings. (Idea: I wonder how to font-lock them differently in Emacs…)

I shouldn't rely only on the colours, since people reading through RSS won't get them and also since some people are colour-blind. Still, the highlights could make my blog posts easier to skim on my website.

Of course, now I want to port Prot's excellent colours from the Modus themes over to CSS variables so that I can have colours that make sense in both light mode and dark mode. Here's a snippet that exports the colours from one of the themes:

(format ":root {\n%s\n}\n"
        (mapconcat
         (lambda (entry)
           (format "  --modus-%s: %s;"
                   (symbol-name (car entry))
                   (if (stringp (cadr entry))
                       (cadr entry)
                     (format "var(--modus-%s)" (symbol-name (cadr entry))))))
         modus-operandi-palette
         "\n"))

So now my style.css has:

/* Based on Modus Operandi by Protesilaous Stavrou */
:root {
   // ...
   --modus-bg-red-subtle: #ffcfbf;
   --modus-bg-green-subtle: #b3fabf;
   --modus-bg-yellow-subtle: #fff576;
   // ...
}
@media (prefers-color-scheme: dark) {
   /* Based on Modus Vivendi by Protesilaous Stavrou */
   :root {
      // ...
      --modus-bg-red-subtle: #620f2a;
      --modus-bg-green-subtle: #00422a;
      --modus-bg-yellow-subtle: #4a4000;
      // ...
   }
}
mark { background-color: var(--modus-bg-yellow-subtle) }
mark.green { background-color: var(--modus-bg-green-subtle) }
mark.red { background-color: var(--modus-bg-red-subtle) }

Interesting, interesting…

View org source for this post

Excerpts from a conversation with John Wiegley (johnw) and Adam Porter (alphapapa) about personal information management

| productivity, org, pkm

Adam Porter (alphapapa) reached out to John Wiegley (johnw) to ask about his current Org Mode workflow. John figured he'd experiment with a braindumping/brainstorming conversation about Org Mode in the hopes of getting more thoughts out of his head and into articles or blog posts. Instead of waiting until someone finally gets the time to polish it into something beautifully concise and insightful, they decided to let me share snippets of the transcript in case that sparks other ideas. Enjoy!

John on meetings as a CTO and using org-review

Today I was playing a lot with org-review. I'm just trying to really incorporate a strong review process because one of the things I started doing recently is that this [Fireflies AI]​ note taker that's running in the background. Now, it produces terrible transcripts, but it produces great summaries. And at the bottom of every summary, there's a list of all the action items that everyone talked about associated with the names.

So I now have some automation, that will all I have to do is download the Word document and then I have a whole process in the background that uses Pandoc to convert it to Org Mode. Then I have Elisp code that automatically will suck it into the file that I dedicate to that particular meeting. It will auto-convert all of the action items into Org-mode tasks where it's either a TODO if it's for me, or if it's a task for somebody else, tagged with their name.

Then, when I have a one-on-one with a person in the future, I now have a one-on-one template that populates that file, and part of the template is under the agenda heading. It uses an a dynamic block that I've written: a new type of dynamic block that can pull from any agenda file. And what it does is it [takes] from all of those meetings, all of the action items that are still open that are tagged with their name.

This has been actually really, really effective. Now, I don't jump into a one-on-one being like, "Well, I didn't prepare so I don't know what to talk about." I've usually got like 10 to 30 items to go through with them to just see. Did you follow up? Did you complete this? Do we need to talk about this more?

I want to incorporate org-review. Scheduling is not sufficient for me to see my tasks. What I need is something that is like scheduling, but isn't scheduling. That's where org-review comes in. I have a report that says: show me everything that has never been reviewed or everything that is up for review.

Then I have a whole Org key space within agenda for pushing the next review date to a selected date or a fixed quantity of time. So if I hit r r, it'll prompt for the date that I want to see that again. But if I hit r w, it'll just push it out a week.

Every day I try to spend 15 minutes looking at the review list of all the tasks that are subject for review. I don't force myself to get through the whole list. I count it as success if I get through 20 of the tasks. Because earlier I had 730 of them, right? I was just chewing on them day by day.

But now I'm building this into the Org agenda population, because in the dynamic block match query, I can actually say: only populate this agenda with the tasks that are tagged for them that are up for review. That way, if we're in the one-on-one and they say, "Oh I'm working on that but I won't get to it for a month," I'll say, "Let's review that in a month." Then next week's one-on-one won't show that tasks. I don't have to do that mental filtering each time.

This is something I've been now using for a few weeks. I have to say I'm still streamlining, I'm still getting all the inertia out of the system by automation as much as possible, but it's helping me stay on top of a lot of tasks.

I'm surprised by how many action items every single meeting generates. It's like, it's like between 5 and 12 per meeting. And I have 3 to 7 meetings a day, so you can imagine that we're generating up to a hundred action items a week.

In the past, I think a lot of it was just subject to the whims of people's memory. They'll say, "I'm going to do that," and then… Did they remember to do that? Nobody's following up. Three months later, somewhere, they'll go like, "Oh yeah we talked about that, didn't we?"

So I'm trying to now stem the the tide of lost ideas. [My current approach] combines dynamic blocks with org-roam templates to make new files for every meeting and it combines org-review to narrow down the candidate agendas each time appropriately, and it combines a custom command to show me a list of all tasks that are currently needing review.

Reviewing isn't just about, "Is the thing done?" It's also, "Did I tag it with the right names? Did I delegate? Did I associate an effort quantity to it?" (I'm using efforts now as a way to quickly flag whether a day has become unrealistically over-full.)

I only started using column view very, very recently. I've never used it before. But now that I'm using effort strings, it does have some nice features to it: the ability to see your properties laid out in a table.

Back to table of contents

John on making meaningful distinctions (semantic or operational)

Today's agenda has 133 items on it. I need ways to narrow that agenda down.

I've used a lot of different tasks management philosophies. We're always looking for more efficiency, and we're looking for more personal adaptation to what works for us. I've gone from system to system. What I'm starting to realize is that the real value in all of these systems is that they're different enough from whatever you're using today, that they will force you to think about the system you're making for yourself, that is their value.

That's why I think there should always be a huge variety of such systems and people should always be exploring them. I don't believe any one one system can work for everybody, but we all need to be reflecting on the systems that we use. Somebody else showing you, "Hey, I do it this way" is a really nice way to juxtapose whatever system you're using.

I discovered through reading Karl Voit's articles that there are three principal information activities: searching, filtering, and browsing.

  • Hierarchies assist with browsing.
  • Tagging assist with filtering and keywords.
  • Metadata assist with searching.

Those are the three general ways that we approach our data.

We have to do work to draw distinctions between that data. The whole reason that we're drawing distinctions between that data is to narrow our focus to what is important.

I have over 30,000 tasks in my Org Mode overall. 23,000 of them are TODOs. Several thousand of them are still currently open. I'm never gonna see them all. Even if I wanted to, I'm never gonna see them all. I don't know what to search for. I don't know what the query should be. I have to use tagging and scheduling and categorization and everything. I believe that that is the work of a knowledge worker is to introduce these distinctions. That takes time and it takes effort.

What's really important is to draw meaningful distinctions. Make distinctions that matter.

I could tag things with like the next time I go to Walmart, so that I could do a filtered query to show me all things that I might want to do at Walmart, but is that worth the effort or is just tagging it as an errand enough? Because that list will get within the size range that I can now eyeball them all and mentally filter out the ones that I need for Walmart.

What makes a meaningful distinction? I believe there are two things that make a distinction meaningful. One is semantic, and one is operational.

A semantic distinction is a distinction that changes the meaning of the task. If I have a task that says "Set up Zoom account", if that's in my personal Org Mode, that has one level of priority and one level of focused demand. If it's in my work list, that has a totally different importance and a totally different focused demand. It changes the nature of the task from one that is low urgency (maybe a curiosity) to high urgency that might impact many people or affect how I can get my work done. That distinction is meaningful or semantic. It changes the meaning of the task.

An operational distinction changes how I interact with the task. [For example, if I tag a phone call, I can] group all of my phone calls during a certain time of the day. That changes my nature of interaction with the task. I'm doing it at a different time of day or doing it in conjunction with other tasks. That helps narrow my focus during that section of time that I have available for making calls. It's an operational distinction. if it's changing how you interact with the task.

You're succeeding at all of this if on any given day and any given time, what's in front of your eyes is what should be in front of your eyes. That's what all of this is about. If an operational distinction is not aiding you in that effort, it's not worth doing. It's not meaningful enough to go above the bar.

Back to table of contents

John on examples of distinctions that weren't personally worth it

I'm trying to narrow and optimize down to the minimum distinctions necessary to remain effective. If I can ever get rid of a distinction, I'm happy to do it.

I used to have projects and have categories, or what PARA method calls areas. Projects are different from areas and that they have a definition of completion and they have a deadline, but that's the only distinction. I realized that distinction doesn't do me any good because if it has a deadline, that's the distinction, right?

Calling it an area or calling it a project… I can just have projects without deadlines and then that's good enough. I have a query that shows me all projects whose deadlines are coming up within the next month, and then I'm aware of what I need to be aware of. I don't need to make the distinction between the ones that have and don't have deadlines. I just need to assign a deadline so the deadline was sufficient discrimination. I didn't need the classification difference between area and project.

And then [PARA's] distinction between projects, areas, and archives. I realize that there's only one operational benefit of an archive, and it's to speed things up by excluding archives from the Org ID database or from the org-roam-dbsync. That's it. That's the only reason I would ever exclude archives, because I want to search in archives. org-agenda-custom-commands is already only looking at open tasks. In a way, it's by implication archiving anything that's done in terms of its meaning.

This is all just an example of me looking at the para method and realizing that none of their distinctions really meant something for me.

What was meaningful was:

  • Does it have a deadline?
  • Is it bounded or not bounded?
  • Do I want to included in the processing of items?
  • [Is it a habit?]
Back to table of contents

John on habits

I did decide to draw the distinction of habits. I want them to look and feel different because I'm trying to become more habit-heavy.

I read this really brilliant book called Atomic Habits that I think has changed my life more than any other. I've read a lot of really good time management books but this book far and away has made the biggest impact on my life. One of its philosophical points that it makes that is so profound is that goal-oriented thinking is less successful in the long run than behavior-oriented thinking or habit- or system-oriented thinking. Instead of having a goal to clean your office, have a habit to remove some piece of clutter from your office like each time you stand up to go get a snack. You seek habits that in the aggregate will achieve the goals you seek to do.

I'm trying now to shift a lot of things in my to-do lists that were goals. I'm trying to identify the habits that will create systems of behavior that will naturally lead to those goals. I want habits to be first class citizens, and I want to be aware of the habits I'm creating.

I think the other thing that Atomic Habits did is it changed my conception of what a habit is. Before, I thought of a habit as "using the exercise bike" or something like that, which always made it a big enough task that I would keep pushing it off. Then I would realize I'd pushed it off for six months and then I would unschedule it and give up on it because it was just it would just be glaring at me with a look of doom from my agenda list.

What's important is the consistency, not the impact of any one particular accomplishing of that habit. It's a habit. If I do it daily, it's doesn't matter how much of it I do. So even if it just means I get on the bike and I spin the pedals for three minutes, literally, that's successful completion.

Any time you have a new habit, one of the activities in mastering that habit is to keep contracting the difficulty of the habit down, down. You've got to make it so stupidly small and simple to do, that you do it just for the fun of marking it done in the agenda, right?

I have a habit to review my vocabulary lists for languages that I'm learning. I'm okay with one word. As long as I ran the app and I studied one word, that's success.

What you find happening is that you'll do the one word, and now because you're there, because you're in the flow of it, you're like, "I'll do two. You know, I'm already here. What's the big difficulty in doing two?"

So you make the success bar super low. You're trying to almost trick yourself into getting into the flow of whatever that activity is.

[org-habit org-ql list] So I have all of these habits here, and every single habit on this list is super easy to do. Five minutes is all that it would take, or even one minute for most of them. I use different little icons to group them. It also keeps the title of the habit really small. I found that when the titles were really long. I didn't like reading it all the time. It just was a wall of text. When it's these one word plus an icon, it just kind of jumps out.

Back to table of contents

Adam on the Hammy timer and momentum

I took that to a bit of an extreme sort of with my my package remote called Hammy, for hamster. It's for timers and ideas, kind of like being a hamster on a hamster wheel.

Anyway, one of the timers is called flywheel mode. The idea is: just do a little bit. Like, if I'm just having a mental block, I can't stand working on that test today, I'm going to do five minutes. I can spend five minutes doing whatever. Next time, we do 10 minutes in 15. Pretty soon, I'm doing 45 minutes at a stretch. Maybe when I sit down to do 5, I'll actually do 15. I'm just slowly building up that mental momentum. I'll allow myself to quit after 5 minutes, but I end up doing 20.

Back to table of contents

John on momentum and consistency

Momentum is key. There's a flip side to this whole concept of the value of iterative improvement. The opposite remains also true.

Consistent good is your best ally, and inconsistent bad is also your ally. It's when the reverse is true that you have inconsistent good and consistent bad, that's what leads you into roads of doom.

That never occurred to me before. I would always be one of those people who would set myself up with a goal, like, I want to lose 20 pounds. I would struggle to achieve it. I would be dismayed because of how hard it was to get there, and then you'd have a day when you're like, you get off the wagon and you're like, The game is lost. And then and then you can't get back on again. Whereas now it's like that wagon, it's not so easy to get off of. I have to really make a concerted effort to be consistently bad in order to make things horrible again.

I almost want to change org-habit to have a different kind of visualization, because streaks are not motivators for me. Streaks punish you for losing one day out of 200, right? I don't want a graph that shows me streaks. I want a graph that shows me consistency. If I have 200 days and I've missed five of them, I'm super consistent. Maybe I could do this with colors. Just show a bar with that color, and don't show individual asterisks to show when I did it or when I didn't do it, because I find streaks anti-motivating.

[Discussion about other ways to display habits]

Back to table of contents

John on Life Balance by Llamagraphics

The whole principle around Life Balance [by Llamagraphics]​ was: you take all of your tasks, you categorize them, you associate difficulty to them and priority and everything else. Then it tries to use heuristics to determine if your life is being balanced, [and it percolates certain tasks to the top of your list].

If the system's doing a good job, then your agenda list should always be A-Z pretty much the best order in which you ought to do things. It didn't just do category-based balance, it also did difficulty-based balance. You should only be doing super hard stuff once in a while. You do a hard thing, then you do lots of easy things, then you do a hard thing.

Now, I'm wondering… This idea of momentum is very similar to the idea of balance. "Have established momentum with a system of behavior" is similar to "Have an established balance with all of the tasks that I do related to different activities." Is there a data architecture that would allow me to do both of these things.

The whole idea of making the habits be colors and then sorting them according to the spectrum is literally just to achieve balance among how much attention I'm paying to different habits.

[Discussion about dynamic prioritization]

Back to table of contents

Adam on the structure of his TODO view

My fundamental system right now is there's like two org-ql views. There's the view of tasks that are scheduled for today or have a deadline of today, and then there's a view of tasks that I've decided that they need to be done, but I haven't decided when to do them yet.

[Top list]: I just pick the next task off the list or reschedule if it's not important enough now. But then when that's empty, if it ever gets that way, it's the second view. I decide, okay, there's something I need to do. I can do that on Tuesday. Then it disappears until I need to think about it again.

This separates deciding what to do from when to do. Then I can just switch into my own manager mode for a moment, and then switch into "just put your head down and do the work mode."

[More details]

The top view is basically tasks that have a deadline, that are relevant to now (either deadline today or in the past), or it's an item that I've scheduled to work on today or in the past.

The view below, that is items that have no planning date. I need to give them one, or maybe they can just sit in that list of projects that have no next task. I use a project heading to [note] something that needs to be subdivided if I don't have a next task for it, then that'll show up there to remind me to give it one. Once it has a next task, [that] task would appear instead of the project heading until I schedule it. Anything I've forgotten to schedule yet will show up in that list.

Below that I just have a small window that shows me things. I've completed or clocked in the past week.

And then, another small window shows me anything that's a project status so I can get an overview.

In the work file itself, I have a number of links to org-ql views, like "Show me all my top level projects," "Show me tasks I need to talk to my boss about" or somebody else.

Back to table of contents

John on Org and data consistency

Org Mode is really a database, right? It's a database of of highly structured data that has a lot of associated metadata.

The value of that data requires a certain level of consistency which is work that we have to do. In the same way we do work drawing distinctions, we need to do work to keep that data consistent. Am I using this [property]? Am I using this tag to mean the right thing or whatever? Karl Voit says that one of the most valuable things if you're going to use tagging to organize your data is a constrained tag vocabulary. Make a fixed list. Then it's an error if you tag something and it's not in that list, because you either need to expand the list or you need to choose a better tag. That's really valuable.

Even though I use org-lint on all my org files, I found serious data errors. [The newline before an initial star had been lost], and then Org wouldn't see the entry. I never knew that it wasn't even being a participant in any of my queries. I just didn't know stuff like that.

I created a whole bunch of Haskell libraries that allow me to parse Org Mode data. It's a very opinionated parser. It's a very strict parser. It will not parse data files that do not have the exact shape and text and taxonomy that I want.

I wrote a linting module that basically encodes every single rule that I have ever wanted to apply to my data. Like, in the title of an Org Mode heading. I don't want two spaces. I don't want extra excess white space. That should be a rule right?

[Multiple examples, including when a file had TODO entries but didn't have a TODO filetag.]

My linter makes sure that this rule is consistently maintained. Being able to have an aggressive, thorough, universal consistency throughout all of my org data has really put my mind at ease. I can't break my data because I just won't be able to commit the broken data into git. I find myself adding new linting rules on a weekly basis. The more that I add, the more value my data has, because the more regular it is, the more normal, the more searchable.

Back to table of contents

My takeaways

People:

Comments

TIL about column view in #orgmode thanks to this great post from @sacha

@donaldh@hachyderm.io

Qu’est-ce que ça fait plaisir de lire un article de @sacha (en l’occurrence link) et de découvrir que John Wiegley utilise org-review (https://github.com/brabalan/org-review), un petit truc que j’ai écrit il y a 10 ans…

@brab@framapiaf.org

Very interesting to see Adam and John's workflows. Org is so flexible and powerful. I always learn something new watching other people do org stuff.

Nice article, Sacha!

mickeyp on Reddit

View org source for this post