New SF Symbols for VS Code Touch Bar

Posted

A few posts back, I described my no-code approach to customizing my MacBook touch bar in Visual Studio Code, where I used Apple’s SF Symbols 3 Beta icon library. Now, Apple has release a new beta of SF Symbols that makes the process easier!

Customizing Icons with SF Symbols

Download and install Apple’s SF Symbols 3 Beta. The UI looks like this:

SF Symbols with selected icons for VS Code

One can:

  • search for icons you want to re-use, e.g. “bold”,
  • display icons in dark or light theme - touch bar icons are always light on dark, so choose View > Appearance > Dark,
  • render icons in “multicolor” or monochrome (or palette or hierarchical, don’t ask me),
  • alter the line weight (thickness) - I use Light instead of Regular,
  • edit custom icons, duplicated from the original - right click and select Duplicate as Custom Symbol to create a copy in the Custom Symbols library section,

For a custom (duplicated) multicolor icon, it is possible to set the color of each constituent layer, like for this icon:

SF Symbols showing a multicolor icon with layers

However, most icons do not have layers, so here is how to change the color of a specific part of an icon.

SF Symbols showing an icon with duplicated, colored layers

A vector icon (SVG) comprises multiple “subpaths”, so you need to create a new layer with the path you want to change the color for:

  • make sure the color sidebar on the right is visible - from the menu View > Inspectors > Show Color Sidebar.
  • copy the icon’s subpaths into a new layer with the + button at the bottom of the Color sidebar - do this for each color required!
  • in the original layer, uncheck all items you want to change the color for - here my layer color is “Accent” and subpath 3 is disabled, so that only subpaths 1 and 2 have the accent color.
  • in the duplicated layer(s), change the color and check the subpaths you want to have that color - here subpath 3 will be shown in pink.

Note that you can only edit the layer colors for custom symbols. If you can’t, then remember to “Duplicate as Custom Symbol”!

Finally, to export as PNG:

  • select one or more icons (you can export all in one go), right click > Copy Image
  • in a Finder folder, paste with Ctrl+V

Viola, a custom colored icon! I no longer have to edit the icon, e.g. to invert the icon from black to white!

For the really lazy, this bash command will remove the custom. prefix from all PNG filenames, for f in custom.*.png; do mv $f ${f/custom./}; done

One last tip: I compress my PNGs with ImageOptim. All but 3 of my icons are under 1,000 bytes!

Creating the VS Code Touch Bar

Now to create the touch bar custom extension in Microsoft Visual Studio Code, which I’ve described in detail before. In short:

  1. Create a folder ~/.vscode/extensions/mybyways.touchbar-1.0.0,
  2. Copy the icons to that folder,
  3. Create a file package.json in that folder with the content below,
  4. Re-start VS code, and enable the extension if it isn’t already enabled.
{
    "name": "mybyways-touchbar",
    "displayName": "MyByways Touchbar",
    "publisher": "MyByways.com",
    "version": "0.0.2",
    "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": "list.bullet@2x.png", "title": "Markdown: ToggleUList", "command": "markdownExtended.toggleUList" },
            { "icon": "list.number@2x.png", "title": "Markdown: ToggleOList", "command": "markdownExtended.toggleOList" },
            { "icon": "highlighter@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": "tablecells@2x.png", "title": "Markdown: Paste As Table", "command": "markdownExtended.pasteAsTable" },

            { "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": "terminal@2x.png", "title": "View: Toggle Terminal", "command": "workbench.action.terminal.toggleTerminal" },
            { "icon": "sidebar.right@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": "arrowshape.turn.up.left@2x.png", "title": "Undo", "command": "undo" },

            { "icon": "textformat.abc.dottedunderline@2x.png", "title": "Quick Fix...", "command": "editor.action.quickFix" },
            { "icon": "plus.magnifyingglass@2x.png", "title": "Find Next", "command": "editor.action.nextMatchFindAction" },
            { "icon": "minus.magnifyingglass@2x.png", "title": "Find Previous", "command": "editor.action.previousMatchFindAction" },
            { "icon": "curlybraces@2x.png", "title": "Select to Bracket", "command": "editor.action.selectToBracket" },
            { "icon": "line.3.horizontal.decrease@2x.png", "title": "Toggle Fold", "command": "editor.fold" },

            { "icon": "command.square@2x.png", "title": "Show All Commands", "command": "workbench.action.showCommands" },
            { "icon": "rectangle.bottomhalf.inset.filled@2x.png", "title": "View: Toggle Panel", "command": "workbench.action.togglePanel" },
            { "icon": "sidebar.squares.left@2x.png", "title": "View: Toggle Activity Bar Visibility", "command": "workbench.action.toggleActivityBarVisibility" },

            { "icon": "sidebar.left@2x.png", "title": "View: Toggle Side Bar Visibility", "command": "workbench.action.toggleSidebarVisibility" },
            { "icon": "arrow.left.and.right.circle@2x.png", "title": "View: Toggle Side Bar Position", "command": "workbench.action.toggleSidebarPosition" },
            { "icon": "arrowtriangle.up.square@2x.png", "title": "View: Previous Side Bar View", "command": "workbench.action.previousSideBarView" },
            { "icon": "arrowtriangle.down.square@2x.png", "title": "View: Next Side Bar View", "command": "workbench.action.nextSideBarView" },

            { "icon": "gearshape@2x.png", "title": "Preferences: Open Settings (UI)", "command": "workbench.action.openSettings2" },
            { "icon": "keyboard@2x.png", "title": "Preferences: Open Keyboard Shortcuts", "command": "workbench.action.openGlobalKeybindings" },
            { "icon": "heart@2x.png", "title": "Preferences: Color Theme", "command": "workbench.action.selectTheme" },

            { "icon": "doc.circle.fill@2x.png", "title": "Explorer: Focus on Open Editors View", "command": "workbench.files.action.focusOpenEditorsView" },
            { "icon": "folder.circle.fill@2x.png", "title": "Explorer: Focus on Folders View", "command": "workbench.explorer.fileView.focus" },
            { "icon": "bookmark.circle.fill@2x.png", "title": "Explorer: Focus on Outline View", "command": "outline.focus" },
            { "icon": "calendar.circle.fill@2x.png", "title": "Explorer: Focus on Timeline View", "command": "timeline.focus" },

            { "icon": "character.cursor.ibeam@2x.png", "title": "Explorer: Rename File", "command": "renameFile" },
            { "icon": "doc.badge.plus@2x.png", "title": "File: New File", "command": "explorer.newFile" },
            { "icon": "folder.badge.plus@2x.png", "title": "File: New Folder", "command": "explorer.newFolder" },
            { "icon": "trash@2x.png", "title": "Explorer: Move File to Trash", "command": "moveFileToTrash" },
            { "icon": "questionmark.folder@2x.png", "title": "File: Reveal in Finder", "command": "revealFileInOS" },

            { "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" },

            { "icon": "plus.magnifyingglass@2x.png", "title": "Terminal: Find Next", "command": "workbench.action.terminal.findNext" },
            { "icon": "minus.magnifyingglass@2x.png", "title": "Terminal: Find Previous", "command": "workbench.action.terminal.findPrevious" },
            { "icon": "clear@2x.png", "title": "Terminal: Clear", "command": "workbench.action.terminal.clear" },
            { "icon": "square.split.2x1@2x.png", "title": "Terminal: Split Terminal", "command": "workbench.action.terminal.split" },
            { "icon": "square.on.square@2x.png", "title": "Terminal: New Integrated Terminal", "command": "workbench.action.terminal.new" },

            { "icon": "trash.square@2x.png", "title": "Terminal: Kill the Active Terminal Instance", "command": "workbench.action.terminal.kill" },
            { "icon": "chevron.down.square@2x.png", "title": "Terminal: Focus Next Terminal", "command": "workbench.action.terminal.focusNext" },
            { "icon": "chevron.up.square@2x.png", "title": "Terminal: Focus Previous Terminal", "command": "workbench.action.terminal.focusPrevious" },
            { "icon": "chevron.right.square@2x.png", "title": "Terminal: Focus Next Terminal Pane", "command": "workbench.action.terminal.focusNextPane" },
            { "icon": "chevron.left.square@2x.png", "title": "Terminal: Focus Previous Terminal Pane", "command": "workbench.action.terminal.focusPreviousPane" }
        ],
        "menus": {
            "touchBar": [
                { "command": "workbench.action.showCommands", "group": "a@1" },
                { "command": "workbench.action.terminal.toggleTerminal", "group": "a@2" },
                { "command": "workbench.action.toggleActivityBarVisibility", "group": "a@3" },
                { "command": "workbench.action.toggleSidebarVisibility", "group": "a@4", "when": "sideBarFocus" },
                { "command": "editor.action.toggleMinimap", "group": "a@5", "when": "sideBarFocus" },
                { "command": "workbench.action.previousSideBarView", "group": "a@6", "when": "sideBarFocus" },
                { "command": "workbench.action.nextSideBarView", "group": "a@7", "when": "sideBarFocus" },

                { "command": "markdownExtended.toggleBold", "group": "b@1", "when":"editorFocus&&editorLangId==markdown" },
                { "command": "markdownExtended.toggleItalics", "group": "b@2", "when":"editorFocus&&editorLangId==markdown" },
                { "command": "markdownExtended.toggleMark", "group": "b@3", "when":"editorFocus&&editorLangId==markdown" },
                { "command": "markdownExtended.toggleUList", "group": "b@4", "when":"editorFocus&&editorLangId==markdown" },
                { "command": "markdownExtended.toggleOList", "group": "b@5", "when":"editorFocus&&editorLangId==markdown" },

                { "command": "editor.action.toggleWordWrap", "group": "c@1", "when": "editorFocus&&editorLangId!=markdown" },
                { "command": "editor.action.indentLines", "group": "c@2", "when": "editorFocus&&editorLangId!=markdown" },
                { "command": "editor.action.selectToBracket", "group": "c@3", "when": "editorFocus&&editorLangId!=markdown" },
                { "command": "editor.fold", "group": "c@4", "when": "editorFocus&&editorLangId!=markdown&&foldingEnabled" },

                { "command": "editor.action.quickFix", "group": "d@1", "when": "editorFocus&&editorHasCodeActionsProvider" },
                { "command": "undo", "group": "c@2", "when": "editorFocus&&editorLangId!=markdown" },
                { "command": "editor.action.nextMatchFindAction", "group": "d@3", "when": "editorFocus||findInputFocussed" },
                { "command": "editor.action.previousMatchFindAction", "group": "d@4", "when": "editorFocus&&editorLangId!=markdown" },
                { "command": "workbench.action.terminal.findNext", "group": "d@5", "when": "terminalFindFocused||terminalFocus" },

                { "command": "workbench.action.terminal.clear", "group": "e@1", "when": "terminalFocus" },
                { "command": "workbench.action.terminal.split", "group": "e@2", "when": "terminalFocus" },
                { "command": "workbench.action.terminal.new", "group": "e@3", "when": "terminalFocus" },
                { "command": "workbench.action.terminal.kill", "group": "e@4", "when": "terminalFocus" },
                { "command": "workbench.action.terminal.focusNextPane", "group": "e@5", "when": "terminalFocus" },
                { "command": "workbench.action.terminal.focusNext", "group": "e@6", "when": "terminalFocus" },

                { "command": "renameFile", "group": "f@1", "when": "focusedView=='workbench.explorer.fileView'&&!explorerResourceIsRoot&&!explorerResourceReadonly" },
                { "command": "revealFileInOS", "group": "f@1", "when": "focusedView=='workbench.explorer.fileView'" },
                { "command": "explorer.newFile", "group": "f@2", "when": "focusedView=='workbench.explorer.fileView'" },
                { "command": "explorer.newFolder", "group": "f@3", "when": "focusedView=='workbench.explorer.fileView'" },

                { "command": "workbench.action.files.newUntitledFile", "group": "g@1", "when": "!editorIsOpen" },
                { "command": "workbench.action.openSettings2", "group": "g@2", "when": "!editorIsOpen" },
                { "command": "workbench.action.openGlobalKeybindings", "group": "g@3", "when": "!editorIsOpen" },
                { "command": "workbench.action.selectTheme", "group": "g@4", "when": "!editorIsOpen" }
            ]
        }
    }
}

You can inspect the code to discover:

  • which icons I use - the filenames match SF Symbols,
  • the icon assignment to existing command IDs in the contributes.commands section - I have more than needed in the example above, just in case you want to change them, but its better to remove anything you don’t use.
  • the order of touch bar items, sorted by group name, in the menus.touchBar section,
  • and most importantly, the when clause, which defines when each item is displayed. This is where the magic happens, to make the touch bar items context sensitive!

I’m using Markdown commands from an Extension called Markdown Extended by jebbs. Read more about my VS Code setup for markdown note-taking here. You will have to change these if this is not installed in your setup.

My VS Code Touch Bar

I keep tweaking my touch bar, and as of now, it looks like this.

Remember that if there is insufficient space for all the items in a group, then entire group will not be displayed!

No Editor

When no editor is opened, my touch bar is:

VS Code Touch Bar when no Editor is Open

The first “global” group is:

  • command palette
  • toggle terminal
  • toggle activity bar - I often hide this when just taking markdown notes

The second group is shown only when there is no document open:

  • new document
  • show settings UI
  • show keyboard bindings UI
  • show theme picker dropdown

Markdown

VS Code Touch Bar when Editor with Markdown has Focus

When editing a markdown file, a markdown specific group appears after the global group:

  • bold
  • italic
  • mark (highlight)
  • bullet list
  • numbered list

Followed by a “tools” touch bar group:

  • quick fix
  • find

Code

VS Code Touch Bar when Editor has Focus

When editing other files, commands I find useful for general coding appear next:

  • word wrap
  • indent
  • match braces
  • toggle fold

Followed by the expanded tools group:

  • quick fix
  • undo
  • find next
  • find previous

Debugging

VS Code Touch Bar when Debugging

When debugging first group is VS Code’s default, followed by my global group.

Terminal

VS Code Touch Bar when Terminal has Focus

When terminal has focus, Terminal specific stuff appears after the global group:

  • clear (not close!)
  • split
  • new (tabbed) terminal
  • close
  • next split terminal
  • next (tabbed) terminal

Nothing much is left of tools group, except:

  • find in terminal

Folder Explorer and Side Bars

VS Code Touch Bar when Folder Explorer has Focus

When the sidebar has focus, the global group expands with more UI-related commands:

  • toggle sidebar (on the left, after the activity bar)
  • toggle minimap (on the right)
  • next sidebar view in the activity bar
  • previous sidebar view in the activity bar

Then, file-related stuff when specifically the folder explorer side bar has focus:

  • rename file or folder
  • open in finder
  • new file
  • new folder

Closing

Cool? Remember to give credit where credit is due, as far as I know, I’m the first person to figure this out!