I have tried diagramming on a couple posts but never had a good workflow because I didn’t want Mermaid.js with a massive payload, but maybe D2 is a contender!?

I watched this excellent video: Beautiful diagrams in Emacs by Torstein Krause Johansen and it got me interested in reexamining an easy way to integrate diagramming that is both declarative and tangled out, my Mermaid attempt wasn’t that.

Yak shaves

I’ve started this blog post as a way to try it out directly in my workflow, so this is happening live and already has yak-shave vibes. So, let’s see how many yaks we shave:

  1. Oh yeah, just realized that to actually test it fully I need to update my custom build OS image to add the d2 CLI tool and getting it pushed up to my container registry and pulling it down to the laptop to test.
  2. It was a yak-shave after all since elpa.gnu.org kept failing until I realized that it was not code but the remote machine was on VPN 😭. But now I have d2 in my build environment.
  3. I struggled with trying to write a file and then include it and then realized that tangling it inline also includes it in the export, which is cool. But tangling my whole file isn’t doing the job, manually tangling the block does work. * many minutes later * Right, that doesn’t work because of my workflow of org > ox-hugo > md > hugo.
  4. Had to go through and update my Hugo templates to handle SVG images (since resize for thumbnails doesn’t make sense and hence fails).
  5. The org file still isn’t tangling the d2 block in the build pipeline but it does tangle out other blocks…. arrrggghhh!
  6. Alright, it needs to tangle with the rest of the file but not rendered inline, small yak shaved with :exports none :noeval
  7. Woof, now I have to update the Hugo shortcode so it doesn’t try to resize a svg.
  8. :flags or :cmdline isn’t supported by d2-mode’s org-babel-execute:d2 function natively, so all cli parameters (like themes) need to be passed via d2-flags and can’t easily be customized per block.
1
(setq d2-flags (list "--sketch" "--layout" "elk" "--theme" "102" "--dark-theme" "200"))

Post completion reflection: 1 and 2 were completely unnecessary and would have been obvious if I thought through how I handle images and I wouldn’t use the auto-inlining because I add images as figures with alt-text. 7 and 8 were the only real dependencies, the rest was learning churn… a bit sad/frustrating but definitely learned stuff.

D2 Diagrams

Alright now I can finally try D2 diagramming (in my workflow). Here’s the most basic one and it’s reasonably small overhead (now that I have shaved a heard of yaks) and can be reduced further if I made another short code to add in the Hugo image boiler plate and read in org properties to reduce manual entry effort.

1
2
foo -> bar: read
bar -> foo: write
Two boxes named foo and bar with arrows pointed to each other with lables read and write
Simple D2 diagram

Styling is fairly simple but I can’t figure out to show to style all connectors but not shape borders.

1
2
3
4
5
6
7
8
9
direction: right
*.shape: circle
foo -> bar: read
bar -> foo: write{
style: {
      animated: true
      stroke-dash: 3
  }
}
Two cirlces named foo and bar with arrows pointed to each other with lables read and write in horizontal orientation where the 'write' arrow is dashed and animated
Styled and animated D2 diagram

Webring diagramming

So if I were to recreate the webring diagram which started this “I wonder if” thought HOURS ago, it would look like:

1
2
3
4
5
6
7
direction: right
*.shape: circle
A <--> B
B <--> C
C <--> D
D <--> E
E <--> A
Five circular nodes have a bidirectional connection to their immediate neighbors. Nodes are named A, B, C, D, E.
Webring diagram in D2

And when I was trying to explain a broken webring, I could get quite fancy without that much effort:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
direction: right
classes: {
broken: {
  style: {
            stroke-dash: 4
            stroke: "#884444"
            }
  }
working: {
  style: {
    animated: true
            stroke: "#44AA44"
            stroke-dash: 1
            }
  }
}
*.shape: circle
A <--> B: ✅ { class: working }
B <--> C: ❌ { class:broken }
C.style.fill: "#884444"
C <--> D: ❌ { class:broken }
D <--> E: ✅ { class: working }
E <--> A: ✅ { class: working }
Same ring as before but node C is broken and colored red and so are it's connections, all other connections are green and animated.
Broken Webring

So in conclusion: D2 is great, now that the infrastructure is set up and I have a tiny bit of familiarity with it, it is fairly easy to use. I still have to tangle out a file and then include it manually but that could be templated/automated but this side quest ended up being my most of my day so I’m putting a pin in it.