Newbie guide to creating your own slash commands (v2)

Overview

The following guide pertains to SilverBullet v2

I am really new to SilverBullet, been using for a couple of days now, but can see how it is going to impact the way I work.

I followed the Journal Tutorial in the Manual to get started with journaling. It’s a great starting point. I wanted a little more though, to be able to add a time entry for when I was attending meetings for example.

Then I fell down a rabbit hole and ended up in the land of confusion. I wanted to add two / commands:

  • /time - to insert the current time in 24 hr format
  • /timestamp - to insert the current date and time

In the end, this is how I achieved this

Steps

  • Create a page somewhere to host new slash commands
  • Make the page a meta page to hide it from normal page search / listings
  • Create the space-lua functions to provide required content
  • Register slash commands to invoke these functions

Since doing all this and reading more, it may be more appropriate to store the commands page in Library, I leave that to the reader and also point out that you can always simply rename the page to move it there.

Step 1 - create the page

Same as always, CTRL-k, enter page name and SHIFT-Enter to create it - I used silverbullet/commands as name of my page.

Step 2 - make the page a meta page

Meta pages are hidden away from normal page search, nothing special, just add a #meta tag at the top of the page. Done!

Step 3 - add the functions

After the #meta tag, a space-lua block is required. Easiest way to add that is /space-lua command.

I did not want to create global functions and it took a couple of experiments to convince myself that local functions could still be registered in same space-lua block as slash commands. I ended up with the following code block:

local function mvm_get_24h_time()
  return os.date("%H:%M")
end

local function mvm_get_timestamp()
  return os.date("%Y-%m-%d %H:%M")
end

NOTE: mvm are my initials, when I started I though these were going to be global functions and wanted to avoid conflicts - old habit!

Sure, there are probably a million ways to improve on this code, but it gets the job done!

Step 4 - register slash commands to invoke these functions

In the same space-lua block, after the functions, add the following code to register slash commands /time and /timestamp:

slashCommand.define {
  name = "time",
  run = function()
    editor.insertAtCursor(mvm_get_24h_time())
  end
}

slashCommand.define {
  name = "timestamp",
  run = function()
    editor.insertAtCursor(mvm_get_timestamp())
  end
}

Job done. Now, in any page, you can hit / and follow it with time or timestamp to get the content inserted into your page.

Closing thoughts

I searched numerous examples while trying to get the above working and hit a number of brick walls:

Dark theme

My initial attempt contained bad code, I was trying to register commands using syntax from v1. The entire code block was underlined with a squiggly red line - something is wrong. Hover over that squiggly red line and see something like a tooltip, but it looked empty. Took me ages to realise it was not empty, just that the text was impossible to read - I used the mouse to select the text and suddenly it was readable! Unfortunately, the error message it was showing did not help much, but at least I could read it.

Don’t trust search or AI

The error I mention above was unexpected symbol near '"', like I said, not very useful. Turned out to be me using copy-n-paste code to register the slash command, but entire code block was underlined.

Lesson learnt - don’t trust search or AI. As Zef points out in his YouTube tutorial on Space Lua, look at example code in the Library for insights. For this, I looked at [[Library/Std/APIs/Date]] to see how the /today slash command was registered.

To go find code examples, use shift-ctrl-k to search for meta pages and find examples there.

Thanks for the reminder, I kept running into that issue as well, but never did anything about it until now. If you add a space-style block with:

.cm-diagnosticText { 
  color: #000000; 
}

Or any other dark color, it will make the text legible again.

Cool! I added pretty much the exact same slash command. In case you wanted to remove a step (and avoid the naming/local/global question), you can also just add the os.date call inline! But I can definitely see this approach being useful in some cases as well.

slashCommand.define {
  name = "time",
  description = "Insert the current time",
  run = function()
    editor.insertAtCursor(os.date("%I:%M %p"), false, true)
  end
}