DEV Community

Cover image for Implementing a Command Palette and Task Timer (part 1)
John P. Rouillard
John P. Rouillard

Posted on • Updated on


Implementing a Command Palette and Task Timer (part 1)

I am a developer for the open source Roundup Issue Tracker. It has many use cases. One is to develop issue trackers like GitHub Issues, Bugzilla, or Request Tracker. I also develop a custom issue tracker for a help desk environment. This article describes the steps in adding a task-timing feature for that tracker.

A user requested a task timer. The workflow:

  • open an issue page,
  • start a timer,
  • start the task associated with the issue,
  • use the issue page to document the work.

When done, the user would:

  • stop the timer,
  • finish documentation, attach files, and make other changes to the issue,
  • submit the time and other changes.

There are a few decisions to make:

  • timer functions
    • is start/stop enough?
    • is pause/restart needed?
    • does the user need to change/set/edit the timer?
    • do we need to track seconds, minutes, and hours? Since the customer is billed for the time, recording seconds seems excessive. Is there a scenario where the timer would need to record seconds?
  • timer controls
    • do we use a button/buttons? Does checking a checkbox activate the timer?
    • what does the UI look like for each timer function? Are the controls in the issue page? Are the controls in a floating popup? Does the popup need to be movable/collapsible so it doesn't block access to the underlying issue?
  • notification feedback
    • how is the user notified that the timer is running/paused/stopped?
    • how does the user see the elapsed time?
  • future planning
    • how to add controls without cluttering an already complex interface
    • what impact will this have on adding future feature requests in the same context
    • does this guide us in implementing future workflows

Evaluating a Command Palette

I decided to try to install a command palette. A command palette is a UI interface usually activated by a hotkey. It allows searching and selecting from a context-sensitive list of commands. You might be familiar with the VS Code command palette. Command palettes have many advantages for users:

  • discover useful commands (and their shortcuts)
  • faster than scrolling down a long list of commands
  • keyboard is faster than using the mouse
  • invisible until activated
  • make commands available that wouldn't be important enough to get a button or other UI element

There are many command palette implementations in JavaScript. Some are used with specific frameworks. For example, kbar is a React component and spotlight is a Laravel component. I wanted one that would work with Vanilla JavaScript. I identified two candidates:

  • command-pal - "The hackable command palette for the web, inspired by Visual Studio Code."
  • Ninja Keys - "Keyboard shortcut interface for your website that works with Vanilla JS, Vue, and React."

Ninja Keys uses Lit while command-pal uses Svelte. I don't have any experience with either, so.... Both of them can bind any command on the palette to a hotkey thanks to hotkeys.js. Both are MIT licensed. Command-pal is larger in size, but it also bundles all the libraries it needs. It looks like Ninja Keys loads libraries/modules on demand from CDNs on the internet. Being able to use the library without internet access is a nice feature.

Command-pal promotes itself as "hackable". This usually means flexibility and sometimes simplicity. I like both. Command-pal's feature set wasn't as impressive as Ninja Keys. But it does include a floating button to trigger the command palette on mobile. This is a nice touch as moving the page to access UI elements can be tedious on mobile.

Adding command-pal

As a result, I chose command-pal. Integrating it was easy. I downloaded the file from the CDN. I also downloaded the dark theme from GitHub. I added a script tag and stylesheet link to the top-level page Roundup template file. This makes command-pal available on all the tracker's pages.

To invoke it, I added:

 const c = new CommandPal({
   hotkey: "ctrl+space",
   hotkeysGlobal: true,
   commands: commands,
Enter fullscreen mode Exit fullscreen mode

inside a script tag. The tracker is a web application. Sadly, the classic command palette hotkeys: "ctrl+k" or "ctrl+shift+p" are already used by the browser. I also wanted to activate the palette using the hotkey when focused on an input, select, or textarea. hotkeysGlobal: true should do that, but it didn't work for me in Firefox, Chrome, or Brave). I submitted a pull request to fix it.

The commands array included:

      name: "Exit Command Palette",
      contexts: [ "all" ],
      weight: -10,
      name: "Initial Screen",
      description: "Screen shown after login.",
      contexts: [ "all" ],
      handler: () => (window.location.href = "."),
      shortcut: "ctrl+i",_
      weight: 5,
    }, ...
Enter fullscreen mode Exit fullscreen mode

Besides the fields used by command-pal:

  • name,
  • description,
  • handler,
  • shortcut

I added extra fields:

  • weight
  • contexts

These were inspired by the Superhuman blog on building a remarkable command palette. Among the things they suggest are:

  • order commands by popularity/utility
  • listed commands are context sensitive

The commands are initially sorted by weight (using commands.sort()). This displays the most popular (highest weight) commands at the top of the menu. Displaying the search results sorted by weight is an ongoing project.

The command is shown if its contexts property matches the current context. For example, the task timing commands only make sense when editing an issue. They should not be shown when viewing a list of issues, or a user's profile page. Inspecting the page's URL determines the current context. The code filters the command list, eliminating commands that are not appropriate for the context. Only then is CommandPal invoked.

I hope you have enjoyed learning about command palettes and command-pal in particular. In part 2, we will use command-pal to control the task timer and look at how it integrates with the tracker built using the Roundup Issue Tracker.

Roundup Issue Tracker - Roundup 2.2.0 documentation

A simple-to-use and -install issue-tracking system with command-line, web, REST, XML-RPC and e-mail interfaces. Adaptable to many uses cases. Allows you to customise the look and feel and implement different workflows.


Top comments (3)

naubit profile image
Al - Naubit

Great article, you got my follow, keep writing!

gorkemcetin profile image
Görkem Çetin

Good article. I suggest checking Command Palette resources as well, to get an understanding of other players in this field.

rouilj profile image
John P. Rouillard

Thanks. I didn't come across that resource in my searches.

Your link shows ninja keys and command pal as the only two general libraries. So apparently my searching was pretty complete 8-).

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.