Content AND Presentation

2026-02-16 Toy train timetable improvements – buttons

A few weeks ago I wrote about my toy train timetable clock. I used it with my son a few times since then, and it worked great. However, some things that I imagined would be a good idea turned out to be a bit worse UI-wise.

For example, I noticed that it would be quite useful to be able to pause and resume the time. This is a bit difficult to achieve in my initial design, where instead of keeping track of the “current toy time”, I store the start times (both real and toy) and compute the toy time on every update. I thought that was clever and allowed me to avoid problems with timers being late, but after I read the docs again, I realized that I could just increment the “toy time” on every call to ttt-refresh and the result will be (almost) the same. (Theoretically, if Emacs is really busy, say performs some computations taking a minute, then the previous approach would update the toy time to the current value immediately, and the “new” one would update it four times in 15-second increments in quick succession – but (1) I don’t expect that to ever happen, and (2) even if it sometimes does, it’s not a big problem.)

After spending some time thinking about the new design, I considerably simplified the UI. From now on, the ttt-start command (the main entry point) no longer asks the user for the “real start time” – the user just enters the “start toy time” and the clock starts running. The main innovation (from the user’s perspective) are two buttons below the clock: pause (which is self-explanatory) and restart. The latter works like this: the “start toy time” is stored in the new variable ttt-start-toy-time, and restart just sets the toy time to that value (and refreshes the screen so that the update is visible at once).

As usual, Emacs turns out to be a perfect platform for coding little “apps” like this. Much like the web, you get the whole UI “for free”. In the case of the web, you have HTML, CSS if you want it to look fancier, and JavaScript for handling various “events” (like clicks and key presses). In the case of Emacs, you get the machinery of major and minor modes, the concept of keybindings and things like completion or ASCII art drawings. Both platforms support even pretty sophisticated cases like date/time input. In the case of the web, there is the (infamously bad) <input type="date"> and its relatives <input type="datetime-local"> and <input type="time">. In the case of Emacs, there is the (famously great) org-read-date. Of course, the big upside of the web is that basically everyone who has a computer these days (including phones) has the runtime; the big upside of Emacs is that, well, it’s Emacs, so us Emacsers get the integration with one of the best tools in existence.

That’s it for today, happy hacking!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

2026-02-09 Node modules working as command-line scripts revisited

Over five years ago I wrote a short post about writing JavaScript scripts which also work as CJS modules. I wrote that I didn’t know how to perform a similar trick with ESM modules, and that it might even be impossible. Well, I was wrong. I found a solution in the book Shell scripting with Node.js by Axel Rauschmayer. He proposes the following solution in the chapter about file system paths and file URLs:

import {fileURLToPath} from 'node:url';

const modulePath = fileURLToPath(import.meta.url);
if (process.argv[1] === modulePath) {
    main();
}

(well actually, his solution is a bit more complex, but this is the gist of it – head to his book for more details).

I started using it and it worked great… until it didn’t. It turned out that I was able to trick this code by symlinking to my script and calling the symlink instead. After a bit of poking around, I arrived at this code:

import {fileURLToPath} from 'node:url';
import {realpath} from 'node:fs/promises';

const modulePath = await realpath(fileURLToPath(import.meta.url));
if (await realpath(process.argv[1]) === modulePath) {
        main();
}

Note that it uses top-level await, which Node.js has been supporting for some time now.

From now on, I can both call my ESM script directly (even as a symlink) or import it in other code (for example, unit tests).

That’s it for today, thanks for reading!

CategoryEnglish, CategoryBlog, CategoryJavaScript

Comments on this page

2026-02-02 git-link

I have recently discovered a great little package called git-link. It is a wonderful addition to Emacs’ Git-related capabilities. Its purpose is to create links to various Git repositories (it supports GitHub, BitBucket, GitLab and several others) based on where the point is.

The main entry points are two functions, git-link and git-link-commit. The former creates a link to the file in the current buffer. By default, the link points to the current line; with a prefix argument of - (a single minus), the link contains no line number. This function is really smart – it works in git-timemachine and Magit buffers. (One issue I found is that when a file name was changed and git-timemachine shows a version from before the change, the link contains the “new” name, pointing to a “resource not found” page on BitBucket. Given how file renames are a major pain in Git, this is neither unexpected nor very disappointing.)

The other function, git-link-commit, creates a link to the commit hash at point, and that’s pretty much it. (Well, there is more – for example, git-link-homepage creates a link to the repository’s homepage, which is nice, but probably less useful. There are also a bunch of user options, the most useful being probably git-link-open-in-browser, which opens the link in a browser in addition to showing it in the echo area and putting it on top of the kill ring. Also, the three functions support some more prefix arguments – see their docstrings for details.)

One of the things I found lacking is a git-link-dwim function which would call git-link-commit if the point is on a commit hash and git-link otherwise. Also, recognizing branch names in addition to commit hashes would be pretty nice.

The former is easy to fix:

(defun git-link-dwim ()
  "Call `git-link-commit' if on a hash or `git-link' otherwise."
  (interactive)
  (let ((word (word-at-point t)))
    (call-interactively
     (if (string-match-p "^[a-fA-F0-9]\\{7,40\\}$" (or word ""))
         #'git-link-commit
       #'git-link))))

The latter would require more work. It could use git to list the branches in the repository, then construct a regex matching all of them (Elisp has the wonderful regexp-opt function which does exactly that!) and use looking-at to determine if the point is on a branch name. This would require the point to be on the beginning of the branch name, though. An alternative would be to use (thing-at-point 'filename t) – syntactically, branch names are just filenames (or at least something very similar), so it could work, too. Either way, this is a slightly larger project I am currently not that interested in, so I’ll stay with my git-link-dwim command.

Anyway, big thanks to sshaw for writing this extremely useful package!

CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryGit

Comments on this page

More...

CategoryEnglish, CategoryBlog