Skip to content
vic

MLFlexer/modal.wezterm

Vim-like modal keybindings for your terminal! ✌️

MLFlexer/modal.wezterm.json
{
"createdAt": "2024-04-17T17:13:36Z",
"defaultBranch": "main",
"description": "Vim-like modal keybindings for your terminal! ✌️",
"fullName": "MLFlexer/modal.wezterm",
"homepage": "",
"language": "Lua",
"name": "modal.wezterm",
"pushedAt": "2025-06-30T13:32:49Z",
"stargazersCount": 99,
"topics": [
"keybind",
"keybindings",
"modal",
"plugin",
"vim",
"wezterm"
],
"updatedAt": "2025-11-24T03:21:41Z",
"url": "https://github.com/MLFlexer/modal.wezterm"
}

Vim-like modal keybindings for your terminal! ✌️

Add keybindings which operate with vim-like modal bindings to accelerate your workflow. This plugin adds great opt-in default modes along with optional visual indicators and hints for the keybindings.

Improvements to Wezterms CopyMode with vim keybindings Demo of Copy/Search/Visual mode

Demo of UI mode

I have included some default modes which are opt-in as to improve performance for all users.

Copy mode, with Search and Visual submodes

Section titled “Copy mode, with Search and Visual submodes”

Copy mode Visual mode Search mode

UI mode has vim-like bindings to navigate and modify panes, tabs and other UI elements. UI mode

In scroll mode you can scroll with familiar vim bindings. Scroll mode

It is recommended to do the setup with some Customization. However if you just want to try it out you can follow the Preset

Add the following to the bottom of your config:

local wezterm = require("wezterm")
local modal = wezterm.plugin.require("https://github.com/MLFlexer/modal.wezterm")
modal.apply_to_config(config)
modal.set_default_keys(config)

This will add the keybindings to enter and exit modes:

  • ALT-u to enter UI mode from normal mode
  • ALT-c to enter Copy mode from normal mode
  • v to enter visual mode from Copy mode
  • / to enter search mode from Copy mode
  • ALT-n to enter Scroll mode from normal mode
  • esc or CTRL-c to leave current non-normal mode

Checkout the [keybinding descriptions]!(/defaults/keybinds.md)

  1. Require the plugin:
local wezterm = require("wezterm")
local modal = wezterm.plugin.require("https://github.com/MLFlexer/modal.wezterm")
  1. Add your own mode

There are more examples of key tables and status texts with and without hints in the /defaults directory.

-- example key table
local key_table = {
{ key = "Escape", action = modal.exit_mode("mode_name") },
{ key = "c", mods = "CTRL", action = modal.exit_mode("mode_name") },
{ key = "z", action = wezterm.action.TogglePaneZoomState },
}
-- example right status text
local status_text = wezterm.format({
{ Attribute = { Intensity = "Bold" } },
{ Foreground = { Color = "Red" } },
{ Text = wezterm.nerdfonts.ple_left_half_circle_thick },
{ Foreground = { Color = "Black" } },
{ Background = { Color = "Red" } },
{ Text = "MODE NAME " },
})
modal.add_mode("mode_name", key_table, status_text)
  1. Add you keybind to enter the mode
config.keys = {
-- ...
-- your other keybindings
{
key = "m",
mods = "ALT",
action = activate_mode("mode_name"),
}
}
  1. Add the modes to your config
config.key_tables = modal.key_tables
  1. Change right status text when entering/leaving mode
wezterm.on("update-right-status", function(window, _)
modal.set_right_status(window)
end)

If you want to enable a default mode, then you can add the following:

modal.enable_defaults("https://github.com/MLFlexer/modal.wezterm")
-- "ui_mode" can be replaced by any filename from the /defaults directory
local key_table = require("ui_mode").key_table
local icons = {
left_seperator = wezterm.nerdfonts.ple_left_half_circle_thick,
key_hint_seperator = " | ",
mod_seperator = "-",
}
local hint_colors = {
key_hint_seperator = "Yellow",
key = "Green",
hint = "Red",
bg = "Black",
left_bg = "Gray",
}
local mode_colors = { bg = "Red", fg = "Black" }
local status_text = require("ui_mode").get_hint_status_text(icons, hint_colors, mode_colors)
modal.add_mode("UI", key_table, status_text)
config.keys = {
-- ...
-- your other keybindings
{
key = "u",
mods = "ALT",
action = activate_mode("UI"),
}
}
config.key_tables = modal.key_tables

Checkout the specific lua files to see the keybindings and what functionality each mode exports

To add a custom right status you can use the wezterm.format() function to create a formatted string. You can then add it as an argument when you add your mode:

local custom_status = wezterm.format({
{ Attribute = { Intensity = "Bold" } },
{ Foreground = { Color = bg } },
{ Text = wezterm.nerdfonts.ple_left_half_circle_thick },
{ Foreground = { Color = fg } },
{ Background = { Color = bg } },
{ Text = "Some custom text " },
})
modal.add_mode("mode_name", key_table, custom_status)

You should then add the text to your right status by following the steps in the next paragraph

You can use the modal.enter and modal.exit events to set the right status:

wezterm.on("modal.enter", function(name, window, pane)
modal.set_right_status(window, name)
modal.set_window_title(pane, name)
end)
wezterm.on("modal.exit", function(name, window, pane)
window:set_right_status("NOT IN A MODE")
modal.reset_window_title(pane)
end)

Using the wezterm.on(“update-right-status”, …) event

Section titled “Using the wezterm.on(“update-right-status”, …) event”

Alternatively you can show some other text in the right status by making a simple if statement in your wezterm.on function:

wezterm.on("update-right-status", function(window, _)
if modal.get_mode(window) then -- is nil if you are not in a mode
modal.set_right_status(window)
else
-- your other status
end
end)

Thanks to github.com/twilsoft/wezmode for the inspiration to make this plugin. I have created this plugin as a lua alternative to wezmode, as I wanted to extend wezmode, but with lua instead of typescript.