Antoine Kalmbach's blog

A command line diary user interface using FZF

Some years ago I kept dreaming about a simplistic terminal diary manager. Each diary entry would be a markdown file called YYYYMMMDDD.md, they would be viewed in the terminal using a Markdown renderer, and they would be edited using $EDITOR. A keybinding would allow for creating a new entry for today, and so forth. The UI would look something like this:

+------------------+------------------------------------------------------------+
|20241115.md       |┄A Samos neque auribus cessit tenebant id                   |
|20241114.md       |                                                            |
|20241112.md       |┄┄Dicere hausti                                             |
|20241110.md       |                                                            |
|20241109.md       |Lorem markdownum sopor quanto tenet alta videntur instat    |
|20241108.md       |poenas membra facinus ne piscem tenens iam, o. Iuno Iovis,  |
|20241105.md       |nobis pueri, regnis ingentia tristis Tyriam sopire.         |
|20241104.md       |                                                            |
|20241103.md       |• Arte latus ademit cursus cuiquam                          |
|20241102.md       |• Et inferna signa erant sonat temploque timide             |
|20241101.md       |• Namque pro artus Tyrios fasque                            |
|20241031.md       |                                                            |
|20241030.md       |Quid iussus, et agmen est, exhalat, suo novem licet, ne     |
|...               |adplicat honore. Vultu in stricto spiritus, Iove et iam     |
|                  |dixit constitit nec; tamen. Quibus inustus censuque         |
|                  |utinamque subiere: perfundere diduxit census: ora at nec a! |
|                  |Cum visa secabatur leones paelice in tenebras quoque        |
|                  |Gorgonis, bacis. Vana se nisi prima parte, cum hoc ad       |
|                  |removi, media magnorum tristis.                             |
|                  |                                                            |
|                  |                                                            |
|                  |                                                            |
|                  |                                                            |
+------------------+------------------------------------------------------------+

So on the left you have a file selector and on the right you have a preview window. The first thought I had was I could probably write a TUI application for this, but I didn’t, and forgot about the whole idea. Then recently I came back to this idea and started thinking, what if I could solve this using fzf?

Turns out the answer is yes. Fzf can preview things, it can launch other processes (to edit the past entries), use custom keybindings, and more. So, I quickly came up with this bash script:

jour() {
        ls ~/.jour/*.md |
                fzf -d '/' --with-nth=-1         \ 
                    --preview 'mdcat {} --ansi'  \
                    --preview-window='right:80%' \
                    --bind 'enter:become($EDITOR {}),ctrl-n:become($EDITOR ~/.jour/$(date "+%Y-%m-%d.md"))'
}

Here’s an explanation of the fzf parameters:

  • -d '/' --with-nth=-1: since ls returns absolute paths, to get short filenames for the menu on the left, --with-nth lets you choose what to show on the menu based on the actual data passed to fzf, turning /home/foo/.jour/20241110.md into 20241110.md. -d '/' splits the filename using /, and then get the last (-1) element of the split. Nice to have negative indices!
  • --preview 'mdcat {} --ansi': pass the selection to mdcat, forcing ANSI terminal escape codes.
  • --preview-window='right:80%': give the right window most of the width.
  • bind:...: RET opens the file in $EDITOR, Ctrl+N opens the editor with a file using the current date.

Pretty simple, isn’t it? Here’s a screenshot of it in action:

fzf even supports pagination using Shift+Up/Down! This isn’t the first time I’ve solved problems using fzf, and I suspect this won’t be the last.

Of course developing a custom TUI for this would have been fun, but if I can get 90% of the desired end result using fzf, and I won’t have to learn and bother with a new TUI framework, color me satisfied.

Previous: Been there, done that