Nextjournal’s New Command Interface

Over the past six years, Nextjournal’s feature set has grown substantially. Earlier this year, we realized: The more capabilities we add the harder it gets to make them discoverable in our UIs. Where to put that new button, tab, panel? All of a sudden, UIs start competing with each other for screen real estate, visual emphasis, etc.

We can’t be alone in this, we thought. Others have big feature sets too! What about Office or Mathematica? We decided to survey the UI landscape, hoping to find a principled approach to feature discoverability. This is the story of Nextjournal’s new editing UI. If you just want to skip to what’s new, click here.

The content of this post is also available as a presentation that was held at ClojureD, earlier in June. Watch on YouTube.

Surveying the UI landscape

Toolbars

Our journey starts with probably the most common way of presenting features: bars. Popularized by Microsoft since forever, toolbars do a pretty good job showing important features. They are practically the default for interacting with rich text.

A simple formatting toolbar in Apple’s TextEdit.app

They usually rely heavily on icons and reflect the state of the current selection. They can adapt their content to what you’re interacting with. But once you have lots of features, they become really hard to scan. We definitely didn’t want to go into ribbon territory:

What’s wrong with a ribbon or two… (image courtesy of Martin Cole Bourne)

Menu Bars

Menu bars, on the other hand, are limitless in what they can contain. They are a hierarchical representation of your entire feature set with items being enabled or disabled depending on your current selection.

And I would walk 500 miles, and I would roll 500 more…

Their deeply-nested nature can make it arduous to find a feature. To remedy the situation, they present keyboard shortcuts for important features.

Command Palettes

Palettes seem to become ever more popular for navigating large feature sets. They are typically long lists accompanied by a fuzzy search input that makes it easy to find a specific feature. The search may also allow making use of muscle memory as long as the results keep consistent. E.g. typing "Pr ⏎" might always bring you to "Print".

Find the thing you’re interacting with behind the palette…

We were already providing a command palette at Nextjournal but it did little for increasing discoverability. It’s just hard to discover features if you don’t know what you’re looking for. Also, in most cases, they are positioned right over the main content, effectively hiding what you’re interacting with.

Command-Line Interfaces

CLIs are the other end of the extreme. They offer total control over execution, expression and limitless feature sets but they do little in terms of discoverability. Our friends at replit wrote up some really interesting thoughts on how to increase discoverability in CLIs but we decided that this was not an avenue we wanted to pursue.

Looking at the past

With Nextjournal being developed in Clojure and ClojureScript, we also decided to take a look at the rich heritage of LISP editors. Perhaps history would provide us with more insights.

LISP Machines

We start by looking at the 80s LISP machines and here it already gets interesting. These machines where highly interactive. Much more so than what we are used to now from a code editor. Basically, you could click and interact with anything to find out more about it.

This made it necessary for the system to provide you with hints about possible next interactions. For example: Depending on what you move your mouse over, a bar at the bottom would tell you that left-clicking will inspect that value or right-clicking may find a function definition, etc.

As you hover an item, the hints invite you to "click to inspect" (from Rainer Joswig’s video "Interaction with Symbolics Genera Listener & Inspector")

The hints will change as you hover inspected values

Hints for scroll interactions

These hints were omni-present. And while their usefulness varies depending on the situation, it’s clear that the system tracks context at all times so that it can provide these hints. This was an important learning for Nextjournal’s UI.

Emacs & which-key

Continuing from there, we take a look at the original LISP Machine’s most famous evolution: Emacs. Emacs is known for its wealth of keybindings (or chord combinations rather) which can be hard to keep track of and often pose a steep learning curve for beginners. The community-developed which-key package aims to make this easier by displaying available keybindings in a little popup.

For example: Once you press a keybinding, e.g. Ctrl-X (C-x), the package will provide you with possible further combinations.

Possible combinations for C-x- (image from which-key’s README on GitHub)

This package is especially neat in that it not only provides you with a list of "possible next steps" but it also produces a mnemonic effect for frequently presented combinations.

A new editing UI

Looking at all these paradigms, one thing stuck out: it’s all a matter of context. The most clever UI isn’t worth anything if it doesn’t apply to the situation at hand. So adapting to different contexts is what we decided to make core of our new editing UI.

Deriving context

To deliver on that, we first had to do a better job of keeping track of different contexts by observing all kinds of interactions, like:

  • the page you are currently looking at (e.g. editor, dashboard, …),

  • text selections (e.g. Is the cursor in a code cell? Is it in a rich text block?),

  • any open modal dialogs (e.g. the history modal, or the outline),

  • runtime states (e.g. Is anything executing right now?),

  • and many more other aspects.

This gives us a rich collection of properties that we can use to determine wether a feature is applicable to the current situation or not.

Applying context to commands

The second big ingredient is commands. Commands are an abstraction that ties an action to a set of requirements. These requirements can either be a set of keys from the current context or a more complex predicate function:

(commands/register! :code/reference-file
  {:requires #{:editor? :code-cell.focused/id ::node-view/view}
   :keys "Shift-Mod-E"
   :title "Reference file"
   :action (comp code/insert-inline! ::node-view/view)})
ClojureScript

Optionally, commands can also have:

  • a keybinding that will become active only if the command satisfies its context requirements,

  • subcommands (that can fetch their data asynchronously),

  • and various other UI-specific meta data like a category, subcommands, etc.

Commands (and their context-specific states) are available globally in the Nextjournal system and can be used for any piece of UI. For example, a button can now say:

;; Give me the "Run" command …
(let [run-command (commands/get-command :run/run)]
  [:button ;; … and execute it on button click
   {:on-click #(commands/eval-command run-command)}
   "Run"])
ClojureScript

As such, commands (along with its context-dependent state) can be flexibly re-used in multiple different places.

This simple, principled abstraction is the foundation of what became the new Nextjournal editing UI — ultimately, an amalgamation of many of the above paradigms:

Command bar

The central piece of our new UI is a single command bar that lives at the bottom of the screen:

The bar will present you with shortcut commands that change depending on what you’re interacting with. These shortcuts can be stateful. If you place your text cursor into bold rich text, the bold shortcut lights up, similar to what a formatting toolbar would do.

"Bold" lights up as the cursor moves into bold text

Along with that, shortcuts will also present you with keybindings, effectively acting as an always-visible cheat sheet. If you’re ever wondering what to do next, shortcuts will present you with your most likely options.

With the history modal open, shortcuts adapt to history navigation commands

Palettes

Pressing Cmd-J will bring up a multi-column, palette-like interface with all currently available commands. These are sorted depending on the current context. For example, if your cursor is inside a Clojure code cell, opening the palette will show you Clojure structural editing commands first.

Code & Clojure editing commands show up first with the cursor inside a Clojure cell

Again, command items will also present you with any associated keybindings for a cheat sheet-like effect.

Above the palette, a fuzzy search input allows you to quickly find a command. We are trying to keep search paths stable for consistent results.

Depending on the use case, palettes will change their layout to what makes most sense. For example, when inserting a new block (Cmd-E), the palette will transform into a vertical panel to minimize overlap with the editing area so you can see where you’re inserting the new block.

The Insert Block palette retreats to the side so as to not hide any content

When browsing notebooks (search for the "Go to notebook" command), the palette will transform into a tabular notebook listing that shows metadata like last edit or publish dates.

The notebook browser palette

Subcommands

Palettes are hierarchical in their nature, meaning commands can also have subcommands. The little chevron in command items indicates that you can drill down further.

Once you’re in a subcommands listing, the search field will indicate the current hierarchy with a breadcrumb. Use Backspace to jump back into the previous listing.

By default, palettes are ordered by category and will show you the five first items followed by a More > command. Use it to see all commands that belong to a category.

Context Menus

While you can execute any command through the command bar, some people prefer mouse interactions under certain circumstances. For this we added context menus that make use of the same underlying commands & context system. For example, right clicking anywhere outside the main editing area will bring up a menu with default editor commands:

Right-clicking on a block, though, will bring up block-specific commands:

Context menus are tied to Nextjournal’s component hierarchy, conveniently collecting any parent component’s menus into submenus. For example, the block context menu also has all of the editor context menu’s commands available:

Aesthetics

Along with these changes, we also simplified Nextjournal’s editor aesthetics: Visual focus is now on the content, UI chrome is retreated with subdued colors and fewer buttons. We applied this step mainly to the editing interface for now but expect the rest of the site to follow suit soon.

Onwards

We believe this update is a big step towards improving discoverability of features and a more principled way of interacting with Nextjournal. We didn’t limit our commands-based system to the editing UI.

You will find the command bar with its shortcuts on all Nextjournal pages. While commands and shortcuts are currently limited outside of the editor, expect that we will ramp those up soon.

Also, please bear in mind that, while the commands systems should be highly usable by now, you might still run into transitioning issues. If you do so, please let us know! We will continue adding more and more commands & shortcuts everywhere, hoping to make the system ever more discoverable and interactive.

Big thanks goes to Matt Huebert who implemented the larger part of what is now the new commands system, to Jack Rusher for providing tons of ideas, feedback and the historic perspective and to everybody else who relentlessly tested the system and fixed bugs as they came up.