Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>kntnk DSL SupportNew to Visual Studio Code? Get it now.
kntnk DSL Support

kntnk DSL Support

gk62

| (0) | Free
Language support for the kntnk Karabiner-Elements DSL.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

kntnk

What is kntnk?

kntnk is a tool for writing configurations for the Mac keyboard customization tool Karabiner-Elements more easily.

Karabiner-Elements lets you freely change key assignments. However, its configuration file (karabiner.json) uses JSON with deeply nested {} and [], making even small changes cumbersome.

With kntnk, you can express the same configuration concisely and clearly.

Safari
    caps_lock : escape
    f3 : ⌘f

This means, "Only while using Safari, assign Caps Lock to Escape and F3 to ⌘F." kntnk automatically generates karabiner.json from this description.


What You Can Do with kntnk

  • Configure keys with simple syntax — Create configuration files that are easy for anyone to read and write using a concise dedicated notation (DSL).
  • Switch keys by application — Configure keys according to the foreground application, such as using F3 for search in Safari and assigning it differently in VS Code.
  • Specify detailed conditions with languages and variables — Define conditions such as enabling a binding only when the Japanese input language is active.
  • Update while protecting existing configuration — kntnk automatically creates a backup before writing and preserves non-target profiles and the global and devices settings. However, all existing rules in an output target profile are replaced by the generated result.
  • Get real-time editing assistance in VS Code — The VS Code extension provides syntax highlighting, completion, and error reporting so that mistakes can be detected before saving. kntnk can be used with other text editors, but using it together with the VS Code extension is strongly recommended for accurate key-code entry and early error detection.

Installation

Requirements

  • macOS (only Macs with Apple Silicon / M-series chips are supported)
  • Karabiner-Elements must be installed
  • VS Code (or a derivative editor such as Cursor, Antigravity IDE, or Windsurf IDE) — It is not technically required, but writing configurations without completion and error detection is extremely difficult, so it is effectively necessary

Intel Macs (x86_64), Windows, and Linux are not supported.

1. Install the kntnk CLI

Download the executable from the GitHub repository and place it in ~/.local/bin/.

# Create ~/.local/bin if it does not exist
mkdir -p ~/.local/bin

# Download the kntnk binary from the repository and place it here
# (Download it from the latest releases page)
cp kntnk ~/.local/bin/kntnk
chmod +x ~/.local/bin/kntnk

Run the following in a terminal to verify the installation.

kntnk --version

The installation succeeded if a version number is displayed. --version does not read or write .kntnk files or karabiner.json. Run kntnk --help for usage information.

2. Install the VS Code Extension (Recommended)

Search for and install "kntnk DSL Support" in the VS Code Marketplace or Open VSX Registry, or download the VSIX file from the repository and install it manually.

For manual installation:

  1. Download the kntnk-*.vsix file from the repository
  2. Open VS Code and select "Extensions: Install from VSIX..." from the Command Palette (⌘⇧P)
  3. Select the downloaded .vsix file

Usage

Basic Workflow

  1. Create a configuration folder and a .kntnk file
  2. Write key bindings
  3. Move to that folder in a terminal and run kntnk

Step 1: Create a Configuration File

Create a folder anywhere and create a file with the .kntnk extension.

mkdir ~/my-keybindings
cd ~/my-keybindings
touch my-keys.kntnk

Step 2: Write Key Bindings

Open the file in a text editor or VS Code and use the following format.

condition
    input_key : output_key

Example:

// Enabled in all applications
always
    caps_lock : escape

// Enabled only while using Safari
Safari
    f3 : ⌘f
    f4 : ⌘l

// Enabled only while using VS Code
VSCode
    f5 : ⌘⇧f

Everything from // to the end of the line is ignored as a comment.

Writing Conditions

Syntax Meaning
always Always enabled
Safari Specify an application by an alias registered with kntnk
"com.apple.Safari" Specify an application directly by Bundle Identifier
not Safari Enabled outside Safari
language("ja") Enabled when the input language is Japanese

Writing Keys

Write modifier keys as symbols.

Symbol Key
⌘ Command
⇧ Shift
⌥ Option
⌃ Control
FN fn
CL Caps Lock

Use the official Karabiner-Elements names for key codes (for example, spacebar, return_or_enter, and left_arrow).

When the VS Code extension is installed, completion candidates are displayed, saving you the effort of looking up exact names. Modifier-key symbols can also be inserted through completion. For example, start typing command, shift, option, control, fn, or caps_lock in a .kntnk file, then select the candidate to insert ⌘, ⇧, ⌥, ⌃, FN, or CL.

Step 3: Generate karabiner.json

In a terminal, move to the folder containing the .kntnk file and run kntnk.

cd ~/my-keybindings
kntnk

This automatically updates ~/.config/karabiner/karabiner.json. A backup (karabiner_backup.json) is created before the update.

When No Profile Is Specified

If [profile] "..." is not specified in a .kntnk file, kntnk automatically uses kntnk as the Karabiner-Elements profile name.

After running the command, open Karabiner-Elements and select the kntnk profile under Profiles. If the kntnk profile is not selected, the generated key bindings are not applied to the profile currently in use.

To output to a different profile, add the following, for example at the beginning of a .kntnk file.

[profile] "Profile Name"

For questions about configuring Karabiner-Elements or Hammerspoon, refer to their official documentation.

  • Karabiner-Elements Documentation
  • Hammerspoon API Documentation

Editing with the VS Code Extension

When the VS Code extension is installed, the following features are available when you open a .kntnk file.

  • Syntax highlighting — Conditions, key codes, modifier keys, and other elements are color-coded
  • Completion — Key-code, application-name, and modifier-symbol candidates are displayed automatically. Selecting command inserts ⌘, selecting shift inserts ⇧, and so on
  • Error detection — Mistakes appear in the Problems panel before saving
  • Formatting — Files are formatted automatically when saved

You can also generate karabiner.json by running kntnk: Generate Karabiner JSON from the VS Code Command Palette (⌘⇧P).


More Useful Features

Various Actions

In addition to sending key codes, key bindings can perform various actions.

Open a File or URL: open()

open("path or URL") opens the specified file or URL using the standard macOS mechanism.

// Open a file
always
    f1 : open("~/Documents/memo.txt")

// Open a URL
always
    f2 : open("https://example.com")

Variables can be expanded inside string literals using the {variable_name} form.

[set_variable] docs_dir, "~/very_long_name_directory/"

always
    f1 : open("{docs_dir}notes.txt")

To include a double quote in a path or URL, write `". To include a backtick itself, write ``. Escape braces that must not be expanded as variables with `{ and `}.

// Example containing double quotes in the path (actual path: ~/Documents/file"note".txt)
always
    f3 : open("~/Documents/file`"note`".txt")

If open fails, an error message is output and the Sosumi error sound plays.

Run a Shell Command: sh()

sh("command string") runs the specified shell command as written.

// Run a shell command
always
    f3 : sh("pmset displaysleepnow") 

// inside a string literal is not treated as a comment. Commands containing URLs can therefore be written as-is.

always
    f4 : sh("curl https://example.com/api // this is not a comment")

The escaping rules are the same as for open() (`" for a double quote and `` for a backtick).

Run an Applet: applet()

An applet is a small application created as a macOS .app bundle. It can be created with Script Editor (AppleScript), Automator, or a similar tool.

To use applet(), first configure the applet search directory with [set_applets_dir].

// Configure the applet search directory
[set_applets_dir] "~/Documents/MyApplets"

always
    f1 : applet(check_mouse_battery)
    f2 : applet(OpenNotes)

When you write applet(OpenNotes), kntnk searches the configured directory for OpenNotes.app and runs it with the open command (the .app extension is appended automatically).

applet() refers only to directories registered before its position in the source. Using applet() before [set_applets_dir] is an error. If the applet is not found, an error message is output and the error sound plays.

Call a Hammerspoon Function: hs()

Hammerspoon is a powerful macOS automation tool controlled with Lua scripts. hs(function_name) calls a Lua function in the running Hammerspoon instance.

// Call Hammerspoon functions
always
    f4 : hs(myFunction)
    f5 : hs(toggleDarkMode)

Writing hs(myFunction) executes /usr/local/bin/hs -c 'myFunction()'.

Prerequisite: Add loading of the hs.ipc module and definitions of the functions to call to ~/.hammerspoon/init.lua. kntnk cannot call Hammerspoon functions without this setup.

-- ~/.hammerspoon/init.lua
require("hs.ipc")  -- Enable the CLI tool (hs command)

function myFunction()
    hs.alert.show("Hello from Hammerspoon!")
end

function toggleDarkMode()
    hs.osascript.applescript([[
        tell application "System Events"
            tell appearance preferences
                set dark mode to not dark mode
            end tell
        end tell
    ]])
end

The hs command must be installed once. Run the following in the Hammerspoon console (menu bar icon → Open Console).

hs.ipc.cliInstall()

hs() does not work if Hammerspoon is not running.

Do Nothing: nop

nop is a special keyword that explicitly performs no action for an input key. Use it, for example, to disable a key.

always
    f1 : nop

Write nop on the output side (to the right of :) as the only action. It cannot be combined with another key or action or used after @,, which denotes actions performed when the key is released.

// Invalid examples
always
    f1 : a, nop
    f2 : nop, b
    f3 : @, nop

Run When the Key Is Released: @,

When @, appears among the actions, all subsequent actions execute when the key is released.

// Run when the key is released
always
    f13 : set(fn_mode, "1"), @, set(fn_mode, "0")

When F13 is pressed → set fn_mode to "1" When F13 is released → set fn_mode to "0"

For a more advanced example using this mechanism, see "Use Any Key Like a Modifier Key" below.

Organize Configuration Across Multiple Files

You can place multiple .kntnk files in a folder and organize them into subfolders.

my-keybindings/
    global.kntnk       # Configuration that is always enabled
    browsers.kntnk     # Browser configuration
    tools/
        vscode.kntnk   # VS Code configuration

Files placed in a folder named __kntnk_ignore are not loaded. This is useful for moving aside configuration that you want to disable temporarily.

File Combination Order

Multiple .kntnk files are combined in a deterministic order according to the following rules before they are processed.

Basic rule: Traverse "depth-first in folder-name order," and process the .kntnk files in each folder in filename order. When processing a folder, subfolders come first and files directly under that folder come afterward.

Consider the following structure.

my-keybindings/
    browsers/
        chrome.kntnk
        safari.kntnk
    tools/
        vscode.kntnk
    global.kntnk
    shortcuts.kntnk

The files are loaded in this order:

1. browsers/chrome.kntnk     ← Process the browsers/ subfolder first (in filename order)
2. browsers/safari.kntnk
3. tools/vscode.kntnk         ← Process the next subfolder, tools/
4. global.kntnk               ← Finally process files directly under the root in filename order
5. shortcuts.kntnk

The same rules apply recursively to deeper nesting.

my-keybindings/
    a/
        deep/
            z.kntnk    ← 1st (process a/'s deep/ subfolder first)
        a.kntnk        ← 2nd (process files directly under a/ after deep/)
    b/
        b.kntnk        ← 3rd
    root.kntnk         ← 4th (files directly under the root come last)

Situations where this order matters:

Directive Scope

[set_variable] and [set_applets_dir] carry across files. Variables and configured directories defined in one file can be referenced by files loaded later. Conversely, configuration from a file that has not yet been loaded cannot be referenced. When splitting configuration across files, choose folder and file names so that dependent directives are loaded first.

Forward references are not allowed within the same file either. When using a variable in the right-hand side of [set_variable], [set_applets_dir], a condition, or an action, always write [set_variable] first. When switching profiles, variables and applet search directories are retained per profile, and returning to the same profile restores its previous state.

Behavioral Differences Caused by Subset Conditions

When bindings target the same key and their conditions conflict, the one written first takes priority.

// Example 1

Safari
    f1 : sh("echo Running in Safari.")
always
    f1 : nop
// Example 2

always
    f1 : nop
Safari
    f1 : sh("echo Running in Safari.")

When F1 is pressed in Safari, Example 1 displays "Running in Safari.", while Example 2 displays nothing.


Use Any Key Like a Modifier Key

Karabiner-Elements has a feature for detecting simultaneous key presses, but kntnk does not support it. Instead, you can treat any key like a modifier by combining variables and if() conditions.

Basic Pattern

The following example creates key bindings that are enabled only while F13 is held.

Step 1: In an always block, configure the variable to become "1" when F13 is pressed and return to "0" when it is released.

always
    f13 : set(fn_mode, "1"), @, set(fn_mode, "0")

Step 2: In an if(fn_mode, "1") condition block, write the key bindings that should be enabled only while F13 is held.

if(fn_mode, "1")
    h : left_arrow
    j : down_arrow
    k : up_arrow
    l : right_arrow
    semicolon : delete_or_backspace

With this configuration, H/J/K/L function as arrow keys only while F13 is held. Releasing F13 restores normal key input.

Layer Multiple "Modifier Keys"

You can define multiple virtual modifier keys in the same way and create conditions that combine them with and_if().

// Use F13 as fn_mode and F14 as extend_mode
always
    f13 : set(fn_mode, "1"), @, set(fn_mode, "0")
    f14 : set(extend_mode, "1"), @, set(extend_mode, "0")

// fn_mode alone
if(fn_mode, "1")
    h : left_arrow
    l : right_arrow

// Combination of fn_mode + extend_mode
if(fn_mode, "1") and_if(extend_mode, "1")
    h : home
    l : end

Combine with Applications

Combining an application condition with and_if() lets you create bindings that work only when a virtual modifier is active in a specific application.

always
    f13 : set(fn_mode, "1"), @, set(fn_mode, "0")

VSCode and_if(fn_mode, "1")
    h : left_arrow
    j : down_arrow
    k : up_arrow
    l : right_arrow

Notes

It is syntactically possible to use actual modifier keys (command, shift, control, option, and fn) themselves as triggers that switch variables with set(). However, because kntnk does not automatically add optional: ["any"], simultaneous presses with other keys may not work correctly. Using normal keys such as f13 through f20 as virtual modifiers is recommended.


Common Mistakes

Incorrect key-code names

The Space key is spacebar, not space; Enter is return_or_enter, not enter; and Left Arrow is left_arrow, not left. You can confirm the correct names using completion and error reporting in the VS Code extension.

Wrong modifier-key symbol

⌃ (U+2303) and ^ (caret) are different characters. Always use ⌃ for the Control key. Use the extension's completion when entering it in VS Code.

Conditions are not applied

Make sure Karabiner-Elements is running. After generation, also verify that the profile is selected in the Karabiner-Elements settings.

applet() does not work

Verify that [set_applets_dir] is declared before applet(). If [set_applets_dir] is in another file, file loading order may cause it to be loaded after applet().

hs() does not work

Make sure Hammerspoon is running. Also verify that init.lua contains require("hs.ipc") and that the hs command has been installed with hs.ipc.cliInstall().

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft