Building on my introductory post about maintaining “shortlog” diary files, I quite often want to have a look at recent shortlog files.

I have my shortlog folder indexed by LaunchBar, so I can browse the files that way. By default these are listed in lexicographically ascending order. You can reverse this by holding down command when navigating into the folder in LaunchBar.

However, that doesn’t resolve the problem that it’s not always easy to realise that 2020-04-13 is “Monday”.

So I thought I would write a LaunchBar action to do both.

Despite being my first, it was really very easy. I wrote it in JavaScript as the documentation leads you that way, and you don’t have to serialise to JSON and write to stdout to return values. LaunchBar provides some additional tools to make interacting with the system straightforward from JavaScript. (And, to be clear, this isn’t JXA either.)

Here are the settings in the scripts pane in LaunchBar’s Action Editor:

A screenshot of the scripts pane settings in LaunchBar’s action editor for a default script, showing the default settings except that it returns an Item

This is a default script, taking no input. One thing to note is that it returns a result of type “Item” — an array of JavaScript objects with particular keys. You can read about the properties items can have in the LaunchBar action developer documentation.

And here’s the code:

 1function run(argument) {
 2  const shortlogDir = "~/Dropbox/notes/shortlog/"
 3  return File.getDirectoryContents(
 4    shortlogDir
 5  ).filter(fileName =>
 6    /^shortlog-20\d{2}-\d{2}-\d{2}.txt$/.test(fileName)
 7  ).map(fileName => {
 8    const isoString = fileName.substring(9, 19)
 9    const dateString = LaunchBar.formatDate(
10      new Date(isoString),
11      {
12        timeStyle: "none",
13        dateStyle: "full",
14        relativeDateFormatting: true
15      }
16    )
17    return {
18      title: dateString,
19      path: shortlogDir + fileName,
20      /* Use the ISO string as LaunchBar rejects returned objects
21         containing types other than strings, numbers, arrays
22         and objects (dictionaries) */
23      date: isoString
24    }
25  }).sort(
26    /* Note this is a string comparison, but it’s OK as
27       we’re comparing ISO date strings. */
28    (first, second) => first.date > second.date ? -1 : 1
29  )
30}

This is just a “default script”, and while run takes an argument, it’s ignored.

We treat this as a simple pipeline of transformations on data:

  • reading the contents of a directory (lines 3 & 4)
  • keeping only those whose filenames match dated shortlog files (lines 5 & 6)
  • creating an object containing a human-readable date, the path to the file, and an ISO-format date string (lines 7–24)
  • sorting those objects on the ISO date string, newest to oldest (lines 25–29).

In line 3, we make use of the File object, which is provided by LaunchBar. And in lines 9–12 we use the formatDate function on the LaunchBar object, which gives us access to the system’s date formatting, which is both locale-aware and respects the user’s date & time preferences.

A screenshot showing the resulting list of files in reverse date order in LaunchBar

By providing the path in the result object, LaunchBar treats each entry in the list as a file, so you can press return to open it, or press the right arrow to inspect its details.

One oddity: I did try to store a JavaScript Date in the result objects, to use in the final sorting, but LaunchBar displays an error if a Date in the returned items. You could strip out any of your custom properties, but here I just use the ISO-format date string from the filename as it works just as well for the sorting comparison.

(Another oddity: I initially created the Date (now in line 10) manually with the date components extracted from the ISO-format date string. I learned that the constructor doesn’t take a month argument but instead a monthIndex, in the range 0–11. Some background on why this is.)