Neovim GUI Smooth like Yogurt!
Note
The software is still in pre-release, use at your own risk!

Neogurt is a fast and efficient Neovim GUI written in C++23 and WebGPU. It is currently supported only on macOS, support for other platforms are to be determined. Features include:
- Smooth scrolling and smooth cursor out of the box.
- Support for managing multiple neovim sessions.
- Easy to use commands/API.
- Accurate font rendering and color blending.
Planned Features: #2
Known issues: #3
Download the latest release at:
https://github.com/williamhCode/neogurt/releases/latest
Below are the all of the default options:
-- check if nvim is launched by neogurt
if vim.g.neogurt then
vim.g.neogurt_cmd("option_set", {
-- global options (shared across all sessions)
titlebar = "default", -- "default", "transparent", "none"
show_title = true,
blur = 0,
gamma = 1.7,
vsync = true,
fps = 60, -- used if vsync if false
margin_top = 0,
margin_bottom = 0,
margin_left = 0,
margin_right = 0,
macos_option_is_meta = "none", -- "none", "only_left", "only_right", "both"
cursor_idle_time = 10,
scroll_speed = 1,
-- session specific options
bg_color = 0x000000, -- used when opacity < 1
opacity = 1,
})
-- options provided by neovim
vim.o.guifont = "SF Mono:h15"
vim.o.linespace = 0
-- ime highlights set by Neogurt
vim.api.nvim_set_hl(0, "NeogurtImeNormal", { link = "Normal" })
vim.api.nvim_set_hl(0, "NeogurtImeSelected", { link = "Underlined" })
end
Neogurt commands can be invoked in this format:
Neogurt cmd_name arg=value arg1 arg2
For example, to spawn a new session with the name nvim config
and directory ~/.config/nvim
, do
Neogurt session_new name=nvim\ config dir=~/.config/nvim
The lua equivalent is to use the neogurt_cmd
function:
--- @param cmd string: command to send.
--- @param opts table: command options
--- @return any: result of the command
vim.g.neogurt_cmd = function(cmd, opts)
...
end
Let's do the same thing with the lua api:
vim.g.neogurt_cmd("session_new", { name = "nvim config", dir = "~/.config/nvim" } )
Below are all the commands and their arguments
Note that:
arg = value
are optional named argumentsarg = "lua_type"
are required named arguments[i] = value
are optional positional arguments[i] = "lua_type"
are required positional argumentsid = 0
is equivalent to the session id executing the command
local cmds_table = {
-- sets neogurt options
-- default argument values are specified above
-- you can call this anytime, and the options will be updated dynamically
option_set = {},
-- returns session id
session_new = {
name = "",
dir = "~/",
switch_to = true, -- switch to session after creating it
},
-- returns success (bool)
session_kill = {
id = 0
},
-- returns new session id
session_restart = {
id = 0,
curr_dir = false, -- use cwd instead of session dir
},
-- returns success (bool)
session_switch = {
id = "number"
},
-- switch to previous session
-- returns success (bool)
session_prev = {},
-- returns session info table (id, name, dir)
session_info = {
id = 0
},
-- returns list of session info tables
session_list = {
sort = "id", -- id, name, time (recency)
reverse = false,
},
-- spawns a session picker using vim.ui.select()
-- wrapper around session_list
session_select = {
sort = "id",
reverse = false,
},
font_size_change = {
[1] = "number",
all = false, -- change in all sessions
},
font_size_reset = {
all = false, -- change in all sessions
},
}
Important
Aside from option_set
, don't call any Neogurt commands directly during startup!
Lastly, you can execute code after starting up the application for the first time with
vim.g.neogurt_startup = function()
...
end
Below are some configurations I find useful:
if vim.g.neogurt then
-- all modes
local mode = {"", "!", "t", "l"};
-- change font size
map(mode, "<D-=>", "<cmd>Neogurt font_size_change 1 all=false<cr>")
map(mode, "<D-->", "<cmd>Neogurt font_size_change -1 all=false<cr>")
map(mode, "<D-0>", "<cmd>Neogurt font_size_reset all=false<cr>")
-- session mappings
map(mode, "<D-l>", "<cmd>Neogurt session_prev<cr>")
map(mode, "<D-r>", "<cmd>Neogurt session_select sort=time<cr>")
map(mode, "<D-R>", "<cmd>Neogurt session_restart<cr>")
-- sessionizer (create or select session)
local choose_session = function(startup)
local curr_id = vim.g.neogurt_cmd("session_info").id
local session_list = vim.g.neogurt_cmd("session_list", { sort = "time" })
local cmd = [[
echo "$({
echo ~/;
echo ~/.dotfiles;
echo ~/.config/nvim;
echo ~/Documents/Notes;
echo ~/Documents/Work/Resume stuff;
find ~/Documents/Coding -mindepth 2 -maxdepth 2 -type d | sort -r;
})"
]]
local output = vim.fn.system(cmd)
for dir in string.gmatch(output, "([^\n]+)") do
table.insert(session_list, { dir = dir })
end
vim.ui.select(session_list, {
prompt = "Sessions",
format_item = function(session)
if session.id ~= nil then
if session.id == curr_id then
return "* " .. session.name
else
return "- " .. session.name
end
else
return session.dir
end
end
}, function(choice)
if choice == nil then return end
if choice.id ~= nil then
vim.g.neogurt_cmd("session_switch", { id = choice.id })
else
local fmod = vim.fn.fnamemodify
local dir = fmod(choice.dir, ":p")
local name = fmod(dir, ":h:h:t") .. "/" .. fmod(dir, ":h:t")
vim.g.neogurt_cmd("session_new", { dir = dir, name = name })
if startup then
vim.g.neogurt_cmd("session_kill")
end
end
end)
end
map(mode, "<D-f>", function()
choose_session(false)
end)
vim.g.neogurt_startup = function()
choose_session(true)
end
end
git clone https://github.com/williamhCode/neogurt.git --recurse-submodules
git submodule update # if submodules become out of sync
-
Install the latest version of LLVM Clang
-
Default generator is ninja
-
Dependencies
- Boost
- Freetype
- Harfbuzz
- Others are bundled
Example on macOS:
brew install llvm
brew install ninja
brew install boost
brew install freetype
brew install harfbuzz
Building:
make build-setup
make build
make run # runs executable
make package # builds .dmg file in build/release