This is the first part of a two part series on deploying your own touchbar extension for Visual Studio Code, designed mainly to work with Markdown notes. This post gives you a shortcut method with zero code! The next post will details a more “correct” method. If you don’t already realize - this applies to MacBooks with Touch bars running macOS only.

You may be interested to also read my previous post, Customizing VS Code for Markdown note-taking.

Touchbar

I have an updated post, with my current touch bar and using a newer beta version of SF Symbols 3. This post explains every step in more detail, but do visit the newer post too.

First up, screenshots of my touchbar.

Custom Touchbar Extension for Markdown files When dealing with markdown files in my notes workspace, my touchbar contains:

  • bold
  • italics
  • highlight (mark)
  • bullets
  • numbered list
  • command palette
  • toggle bottom panel (terminal, output, problems, debugger)
  • find next
  • quick fix (for Spell Right spelling corrections)
  • indent
  • word wrap

Custom Touchbar Extension for other files When dealing with other files, don’t show the markdown group. You can customize the taskbar for each different file language!

  • command palette
  • toggle bottom panel
  • toggle sidebar (explorer, search, source control, run and debug, extensions)
  • find next
  • find previous
  • quick fix
  • indent
  • outdent
  • word wrap

Custom Touchbar Extension when no file is open When no file is open, both the markdown group and the editor group are not relevant. More important is a “New untitled file” button.

Pre-requisites

There are a few pre-requisites, including VS Code (at time of writing v1.58.2) with the Markdown Extended extension (v1.0.19) and a touchbar setting changed. Plus you need to get or create icons for your touchbar.

Important: this is a tutorial to make and customize your own extension. It is not available in the VS Code Marketplace. The reason is this: if you want to change the touchbar items, you need to manually the Extension Manifest source file!

Markdown Extended Extension

This extension requires Markdown Extended extension to be installed and enabled. This is because I prefer my custom touchbar to to trigger the Markdown Extended’s commands. To customize VS Code and Markdown Extended for note-taking, see my previous post.

VS Code Settings

I have the VS Code setting keyboard.touchbar.ignored is configured to hide all default touchbar buttons. This gives me more space to customize my own buttons.

VS Code Touchbar Settings on Workspace

You can do this either via the user settings or workspace settings - refer to my previous post on workspace-specific settings in .vscode/settings.json

"keyboard.touchbar.ignored": [
    "workbench.action.navigateBack",
    "workbench.action.navigateForward",
    "workbench.action.debug.start",
    "workbench.action.debug.run"
]

If you don’t ignore the default touchbar buttons, you will simply have less space for custom touchbar items. The touchbar will look something like this, in my case with only 5 additional touchbar items (the last two groups are omitted due to insufficient space):

VS Code Touchbar with space for only 5 items

The commands hidden in keyboard.touchbar.ignored can no longer be used even in the custom Extension Manifest!

Icons

The last pre-requisite is PNG icons for each touchbar item.

Here is a tip to get icons from Apple directly:

  • download and install Apple’s SF Symbols Library 3 Beta for macOS,
  • using the app, search for icons you like, select them View > Copy Image (this copies all images),
  • in a Finder folder, paste (Cmd+V) and you’ll get one or more PNG icon files!

Here are the icons I use: Apple SF Symbols 3 Beta

You need PNG, SVG will not work (despite the documentation). Refer to command icon specifications for the correct sizes.

The icon file name must end with @2x to be considered a retina icon.

One thing you must do is invert the icon colors - the icons in SF Symbols are black, but white works better with the touchbar. You can use Preview invert the colors this way:

  • Open the icon, hit Tools > Adjust Color
  • Just below the histogram, swap the first and last pointers - move the first black pointer all the way to the right, and the right white pointer all the way to the left):

Using Preview to invert colors

Shotcut Method to Create a custom Touchbar Extension

To create an extension the “easy” way do this:

  1. In ~/.vscode/extensions, create a folder to store your extension source code, e.g. ~/.vscode/extensions/mybyways.touchbar-1.0.0/ if you want to follow the standard naming convention.
  2. Create the Extension Manifest, called package.json, as below:
    • modify the Commands (contributes.commands) section to trigger other commands,
    • modify the Menus (contributes.menus.touchBar) section to specify the icons and order of items in the touchbar.
  3. Place all icons in this same folder (no sub-folder required, although you can if you prefer).
  4. Re-start VS Code to test and if prompted, re-load the window to reflect the extension change:

VS Code Re-Load Extension Window

Experts will notice there is no main below and no extension.js file - this is a zero-code method!

{
    "name": "mybyways-touchbar",
    "displayName": "MyByways Touchbar",
    "publisher": "MyByways.com",
    "version": "0.0.1",
    "engines": { "vscode": "^1.50.0" },
    "contributes": {
        "commands": [
            { "icon": "bold@2x.png", "title": "Markdown: Toggle Bold", "command": "markdownExtended.toggleBold" },
            { "icon": "italic@2x.png", "title": "Markdown: Toggle Italics", "command": "markdownExtended.toggleItalics" },
            { "icon": "underline@2x.png", "title": "Markdown: ToggleUList", "command": "markdownExtended.toggleUList" },
            { "icon": "list.bullet@2x.png", "title": "Markdown: ToggleOList", "command": "markdownExtended.toggleOList" },
            { "icon": "123.rectangle.fill@2x.png", "title": "Markdown: Toggle Mark", "command": "markdownExtended.toggleMark" },
            { "icon": "strikethrough@2x.png", "title": "Markdown: Toggle Strikethrough", "command": "markdownExtended.toggleStrikethrough" },
            { "icon": "underline@2x.png", "title": "Markdown: Toggle UnderLine", "command": "markdownExtended.toggleUnderline" },
            { "icon": "textformat.subscript@2x.png", "title": "Markdown: Toggle Superscript", "command": "markdownExtended.toggleSuperscript" },
            { "icon": "textformat.superscript@2x.png", "title": "Markdown: Toggle Subscript", "command": "markdownExtended.toggleSubscript" },

            { "icon": "increase.indent@2x.png", "title": "Indent Line", "command": "editor.action.indentLines" },
            { "icon": "decrease.indent@2x.png", "title": "Outdent Line", "command": "editor.action.outdentLines" },
            { "icon": "magnifyingglass@2x.png", "title": "Find Next", "command": "editor.action.nextMatchFindAction" },
            { "icon": "magnifyingglass.flip@2x.png", "title": "Find Previous", "command": "editor.action.previousMatchFindAction" },
            { "icon": "rectangle.righthalf.filled@2x.png", "title": "View: Toggle Minimap", "command": "editor.action.toggleMinimap" },
            { "icon": "arrow.turn.down.left@2x.png","title": "View: Toggle Word Wrap", "command": "editor.action.toggleWordWrap" },
            { "icon": "textformat.abc.dottedunderline@2x.png", "title": "Quick Fix...", "command": "editor.action.quickFix" },

            { "icon": "terminal@2x.png", "title": "Show All Commands", "command": "workbench.action.showCommands" },
            { "icon": "rectangle.bottomhalf.filled@2x.png", "title": "View: Toggle Panel", "command": "workbench.action.togglePanel" },
            { "icon": "rectangle.lefthalf.filled@2x.png", "title": "View: Toggle Side Bar Visibility","command": "workbench.action.toggleSidebarVisibility" },
            { "icon": "doc.badge.plus@2x.png", "title": "File: New Untitled File", "command": "workbench.action.files.newUntitledFile" },
            { "icon": "character.bubble@2x.png","title": "Change Language Mode", "command": "workbench.action.editor.changeLanguageMode" }
        ],
        "menus": {
            "touchBar": [
                { "command": "markdownExtended.toggleBold", "group": "a@1", "when":"editorLangId==markdown" },
                { "command": "markdownExtended.toggleItalics", "group": "a@2", "when":"editorLangId==markdown" },
                { "command": "markdownExtended.toggleMark", "group": "a@3", "when":"editorLangId==markdown" },
                { "command": "markdownExtended.toggleUList", "group": "a@4", "when":"editorLangId==markdown" },
                { "command": "markdownExtended.toggleOList", "group": "a@5", "when":"editorLangId==markdown" },

                { "command": "workbench.action.showCommands", "group": "b@1" },
                { "command": "workbench.action.togglePanel", "group": "b@2" },
                { "command": "workbench.action.toggleSidebarVisibility", "group": "b@3", "when": "editorIsOpen&&editorLangId!=markdown" },
                { "command": "workbench.action.files.newUntitledFile", "group": "b@5", "when": "!editorIsOpen" },
                { "command": "editor.action.nextMatchFindAction", "group": "b@6", "when": "editorIsOpen" },
                { "command": "editor.action.previousMatchFindAction", "group": "b@7", "when": "editorIsOpen&&editorLangId!=markdown" },
                { "command": "workbench.action.editor.changeLanguageMode", "group": "b@8", "when": "editorLangId==plaintext" },

                { "command": "editor.action.quickFix", "group": "c@1", "when": "editorHasCodeActionsProvider" },
                { "command": "editor.action.indentLines", "group": "c@2", "when": "editorIsOpen" },
                { "command": "editor.action.outdentLines", "group": "c@3", "when": "editorIsOpen&&editorLangId!=markdown" },
                { "command": "editor.action.toggleWordWrap", "group": "c@4", "when": "editorIsOpen" }
            ]
        }
    }
}

To remove the extension, simply remove the whole folder.

Commands

The commands section contains any (and all) items that could appear in the touchbar. What I show above is a superset of some items that are relevant to me. These can be built-in commands e.g. editor.action.* or commands contributed by extensions like markdownExtended.*.

Each command comprises an icon, a title and a command ID:

{ 
    "icon": "bold@2x.png", 
    "title": "Markdown: Toggle Bold", 
    "command": "markdownExtended.toggleBold"
},

To add more commands, simply add a line with the command title and command ID. Here’s a tip to determine an existing command ID:

  • open Code > Preferences > Keyboard Shortcuts
  • search for a command, e.g. "bold" and once found...
  • right-click on the item and Copy Command ID.

Keyboard Shortcuts to get command IDs

I make sure the command title is exactly as displayed, otherwise the extension will override the original command name, e.g. if you use { "icon": "b@2x.png", "title": "B", "command": "markdownExtended.toggleBold" }, then the command name will become just "B" which also affects the Command Palette!

It is not possible to use the in-built Codicons in the touchbar. At least in version 1.58.x, specifying e.g. "icon"="$(bold)" does not work as expected.

Menus

The menus.touchbar section specifies what items appear in the touchbar, and when.

Each touchbar item comprises:

  • a command ID, which much match a command in the previous section,
  • a group that breaks up the touchbar items into sections
    • by default, touchbar items are sorted by the command title - to change the order use the group attribute with the a@1, a@2, a@3... notation.
    • touchbar groups are also sorted by the group name, hence, I start mine with a, b, c... so that the markdown group named a@* appears before b@*, etc.
  • and optionally, when which is the condition wherein the touchbar item is visible i.e. visible when the document language is Markdown:
{ 
    "command": "markdownExtended.toggleBold", 
    "group": "a@1", 
    "when":"editorLangId==markdown"
},

On the touchbar on a 16" MacBook Pro, I can display only about 11 items, with the keyboard.touchbar.ignored setting, depending on the width of team item (text or icon) and the number of groups. If the items exceed the space available on the touchbar, then the entire group will not be shown. You have to experiment.

You may be interested to know the standard two groups are named navigation and 9_debug - see the VS Code source code, editor.contribution.ts and debug.contribution.ts respectively.

Conclusion

I find that Nasc VSCode Touchbar and other Markdown touchbar extensions don’t fit my needs, and especially don’t leverage commands from other extensions. In my case, I prefer to use Markdown Extended’s key bindings to commands, hence my touchbar should leverage those same commands too.

With this simple method, you can also create your own touchbar that leverages any extension you use, for the language(s) of your choice!

The next post will detail how I actually implemented the touchbar extension properly, just so I remember myself (before I realized this simple, zero-code shortcut method worked equally well).

I have an updated post, with my current touch bar and using a newer beta version of SF Symbols 3. This post explains every step in more detail, but do visit the newer post too.