A versatile and easy to use tab-bar written in Lua.
tabline.wez
requires the WezTerm terminal emulator.
Tabline was greatly inspired by lualine.nvim, a statusline plugin for Neovim, and tries to use the same configuration format.
Feel free to create an issue/PR if you want to see anything else implemented, or if you have some question or need help with configuration.
Here is a preview of what the tab-bar can look like.
Some more examples, but very zoomed in.
tabline.wez
supports all the same themes as WezTerm. You can find the list of themes here.
local tabline = wezterm.plugin.require("https://github.com/michaelbrusegard/tabline.wez")
You'll also need to have a patched font if you want icons.
Tabline has sections as shown below just like lualine with the addition of tabs
in the middle.
+-------------------------------------------------+
| A | B | C | TABS | X | Y | Z |
+-------------------------------------------------+
Each sections holds its components e.g. Current active keytable (mode).
tabline.setup({
options = {
icons_enabled = true,
theme = 'Catppuccin Mocha',
color_overrides = {},
section_separators = {
left = wezterm.nerdfonts.pl_left_hard_divider,
right = wezterm.nerdfonts.pl_right_hard_divider,
},
component_separators = {
left = wezterm.nerdfonts.pl_left_soft_divider,
right = wezterm.nerdfonts.pl_right_soft_divider,
},
tab_separators = {
left = wezterm.nerdfonts.pl_left_hard_divider,
right = wezterm.nerdfonts.pl_right_hard_divider,
},
},
sections = {
tabline_a = { 'mode' },
tabline_b = { 'workspace' },
tabline_c = { ' ' },
tab_active = {
'index',
{ 'parent', padding = 0 },
'/',
{ 'cwd', padding = { left = 0, right = 1 } },
{ 'zoomed', padding = 0 },
},
tab_inactive = { 'index', { 'process', padding = { left = 0, right = 1 } } },
tabline_x = { 'ram', 'cpu' },
tabline_y = { 'datetime', 'battery' },
tabline_z = { 'hostname' },
},
extensions = {},
})
If you want to get your current tabline config, you can do so with:
tabline.get_config()
Tabline requires that some options are applied to the WezTerm Config struct. For example the retro tab-bar must be enabled. Tabline provides a function to apply some recommended options to the config. If you already set these options in your wezterm.lua
you do not need this function. This needs to be called after wezterm.setup()
.
tabline.apply_to_config(config)
Caution
This function has nothing to do with the tabline config passed into setup and retrieved with tabline.get_config()
. It only applies some recommended options to the WezTerm config. More info here
tabline.setup()
options = { theme = 'GruvboxDark' }
All available themes are found here.
To modify a theme, you can use the color_overrides
option.
-- Change the background of tabline_c section for normal mode
tabline.setup({
options = {
color_overrides = {
normal_mode = {
c = { bg = '#112233' },
},
}
}
})
This is also where you would specify the colors for a new Key Table (mode). Tabline expects each key table to end with _mode
.
tabline.setup({
options = {
color_overrides = {
-- Default colors from Catppuccin Mocha
normal_mode = {
a = { fg = '#181825', bg = '#89b4fa' },
b = { fg = '#89b4fa', bg = '#313244' },
c = { fg = '#cdd6f4', bg = '#181825' },
},
copy_mode = {
a = { fg = '#181825', bg = '#f9e2af' },
b = { fg = '#f9e2af', bg = '#313244' },
c = { fg = '#cdd6f4', bg = '#181825' },
},
search_mode = {
a = { fg = '#181825', bg = '#a6e3a1' },
b = { fg = '#a6e3a1', bg = '#313244' },
c = { fg = '#cdd6f4', bg = '#181825' },
},
-- Defining colors for a new key table
window_mode = {
a = { fg = '#181825', bg = '#cba6f7' },
b = { fg = '#cba6f7', bg = '#313244' },
c = { fg = '#cdd6f4', bg = '#181825' },
},
-- Default tab colors
tab = {
active = { fg = '#89b4fa', bg = '#313244' },
inactive = { fg = '#cdd6f4', bg = '#181825' },
inactive_hover = { fg = '#f5c2e7', bg = '#313244' } },
}
}
}
})
If you want to get the colors used in the current theme, you can do so with:
tabline.get_colors()
You will get an object like the color_overrides
object above, but with the addition of a scheme property (the scheme property is the scheme object from WezTerm with every color found there).
Tip
This can be useful when creating your own components or extensions and you want to use the same colors as the current theme
tabline defines three kinds of separators:
section_separators
- separators between sectionscomponent_separators
- separators between the different components in sectionstab_separators
- separators around tabs
options = {
section_separators = {
left = wezterm.nerdfonts.pl_left_hard_divider,
right = wezterm.nerdfonts.pl_right_hard_divider,
},
component_separators = {
left = wezterm.nerdfonts.pl_left_soft_divider,
right = wezterm.nerdfonts.pl_right_soft_divider,
},
tab_separators = {
left = wezterm.nerdfonts.pl_left_hard_divider,
right = wezterm.nerdfonts.pl_right_hard_divider,
},
}
Here, left refers to the left-most sections (a, b, c), and right refers to the right-most sections (x, y, z). For the tabs it refers to each side of the tab.
options = {
section_separators = '',
component_separators = '',
tab_separators = '',
}
sections = { tabline_a = { 'mode' } }
Tabline separates components into ones available for the tabline components (tabline_a
, tabline_b
, etc...), which are grouped under Window since they have access to the Window object.
And the tab_active
and tab_inactive
components which are grouped under Tab and have access to TabInformation.
- Window
mode
(current keytable)battery
(battery percentage)cpu
(cpu percentage)datetime
(current date and time)hostname
(hostname of the machine)ram
(ram used in GB)window
(window title)workspace
(active wezterm workspace)
- Tab
tab
(tab title)cwd
(current working directory)parent
(parent directory)process
(process name)index
(tab index)zoomed
(indicator if tab has zoomed pane)
local function hello()
return 'Hello World'
end
sections = { tabline_a = { hello } }
Note
Functions receive the Window
object or TabInformation
object as the first argument depending on the component group
sections = { tabline_a = { 'Hello World' } }
You can find all the available format items here. The ResetAttributes
format item has been overwritten to reset all attributes back to the default for that component instead of the WezTerm default.
sections = {
tabline_c = {
{ Attribute = { Underline = 'Single' } },
{ Foreground = { AnsiColor = 'Fuchsia' } },
{ Background = { Color = 'blue' } },
'Hello World', -- { Text = 'Hello World' }
}
}
Tip
Strings are automatically wrapped in a Text FormatItem when used as a component.
You can use any valid lua expression as a component including:
- oneliners
- global variables (as strings)
- require statements
sections = { tabline_c = { os.date('%a'), data, require('util').status() } }
data
is a global variable in this example.
Component options can change the way a component behave. There are two kinds of options:
- global options affecting all components
- local options affecting specific
Global options can be used as local options (can be applied to specific components) but you cannot use local options as global. Global options used locally overwrites the global, for example:
tabline.setup {
options = { fmt = string.lower },
sections = {
tabline_a = {
{ 'mode', fmt = function(str) return str:sub(1,1) end }
},
tabline_b = { 'window' }
}
}
mode
will be formatted with the passed function so only first char will be
shown. On the other hand window
will be formatted with the global formatter
string.lower
so it will be showed in lower case.
These are options
that are used in the options table.
They set behavior of tabline.
Values set here are treated as default for other options that work in the component level.
For example even though icons_enabled
is a general component option.
You can set icons_enabled
to false
and icons will be disabled on all
component. You can still overwrite defaults set in the options table by specifying
the option value in the component.
options = {
theme = 'nord', -- tabline theme
section_separators = {
left = wezterm.nerdfonts.ple_right_half_circle_thick,
right = wezterm.nerdfonts.ple_left_half_circle_thick,
},
component_separators = {
left = wezterm.nerdfonts.ple_right_half_circle_thin,
right = wezterm.nerdfonts.ple_left_half_circle_thin,
},
tab_separators = {
left = wezterm.nerdfonts.ple_right_half_circle_thick,
right = wezterm.nerdfonts.ple_left_half_circle_thick,
},
}
These are options that control behavior at the component level and are available for all components.
sections = {
tabline_a = {
{
'mode',
icons_enabled = true, -- Enables the display of icons alongside the component.
-- Defines the icon to be displayed in front of the component.
-- Can be string|table
-- As table it must contain the icon as first entry and can use
-- color option to custom color the icon. Example:
-- { 'workspace', icon = wezterm.nerdfonts.cod_terminal_tmux } / { 'workspace', icon = { wezterm.nerdfonts.cod_terminal_tmux, color = { fg= '#00ff00' } } }
icons_only = false, -- Only show icon (if the component has one)
-- icon position can also be set to the right side from table. Example:
-- {'branch', icon = { wezterm.nerdfonts.cod_terminal_tmux, align = 'right', color = { fg = '#00ff00' } } }
icon = nil,
cond = nil, -- Condition function, the component is loaded when the function returns `true`.
padding = 1, -- Adds padding to the left and right of components.
-- Padding can be specified to left or right independently, e.g.:
-- padding = { left = left_padding, right = right_padding }
fmt = nil, -- Format function, formats the component's output.
-- This function receives two arguments:
-- - string that is going to be displayed and
-- that can be changed, enhanced and etc.
-- - Window/TabInformation object with information you might
-- need. E.g. active_pane if used with Window.
},
},
}
These are options that are available on specific components.
For example, you have option on index
component to
specify if it should be zero indexed.
sections = {
tabline_a = {
{
'datetime',
-- options: your own format string ('%Y/%m/%d %H:%M:%S', etc.)
style = '%H:%M',
hour_to_icon = {
['00'] = wezterm.nerdfonts.md_clock_time_twelve_outline,
['01'] = wezterm.nerdfonts.md_clock_time_one_outline,
['02'] = wezterm.nerdfonts.md_clock_time_two_outline,
-- for every hour...
['23'] = wezterm.nerdfonts.md_clock_time_eleven,
},
-- hour_to_icon is a table that maps hours to icons it overwrites the default icon property.
-- To use the default icon property set hour_to_icon to nil.
-- The color and align properties can still be used on the icon property.
},
},
}
sections = {
tab_active = {
{
'cwd',
max_length = 10, -- Max length before it is truncated
},
},
}
sections = {
tab_active = {
{
'index',
zero_indexed = false, -- Does the tab index start at 0 or 1
},
},
}
sections = {
tab_active = {
{
'process',
process_to_icon = {
['apt'] = wezterm.nerdfonts.dev_debian,
['bash'] = wezterm.nerdfonts.cod_terminal_bash,
['bat'] = wezterm.nerdfonts.md_bat,
['cmd.exe'] = wezterm.nerdfonts.md_console_line,
['curl'] = wezterm.nerdfonts.md_flattr,
['debug'] = wezterm.nerdfonts.cod_debug,
['default'] = wezterm.nerdfonts.md_application,
['docker'] = wezterm.nerdfonts.linux_docker,
['docker-compose'] = wezterm.nerdfonts.linux_docker,
['git'] = wezterm.nerdfonts.dev_git,
['go'] = wezterm.nerdfonts.md_language_go,
['lazydocker'] = wezterm.nerdfonts.linux_docker,
['lazygit'] = wezterm.nerdfonts.cod_github,
['lua'] = wezterm.nerdfonts.seti_lua,
['make'] = wezterm.nerdfonts.seti_makefile,
['nix'] = wezterm.nerdfonts.linux_nixos,
['node'] = wezterm.nerdfonts.md_nodejs,
['npm'] = wezterm.nerdfonts.md_npm,
['nvim'] = wezterm.nerdfonts.custom_neovim,
['psql'] = wezterm.nerdfonts.dev_postgresql,
['zsh'] = wezterm.nerdfonts.dev_terminal,
-- and more...
},
-- process_to_icon is a table that maps process to icons
},
},
}
sections = {
tab_active = {
{
'cpu',
throttle = 3, -- How often in seconds the component updates, set to 0 to disable throttling
},
},
}
sections = {
battery_to_icon = {
empty = { wezterm.nerdfonts.fa_battery_empty, color = { fg = scheme.ansi[2] } },
quarter = wezterm.nerdfonts.fa_battery_quarter,
half = wezterm.nerdfonts.fa_battery_half,
three_quarters = wezterm.nerdfonts.fa_battery_three_quarters,
full = wezterm.nerdfonts.fa_battery_full,
}
-- battery_to_icon is a table that maps battery percentage to icons
-- It overwrites the default icon property. To use the default icon property set battery_to_icon to nil
-- The color and align properties can still be used on the icon property
}
tabline extensions change statusline appearance for other plugins.
By default no extensions are loaded to improve performance. You can load extensions with:
extensions = { 'resurrect' }
- resurrect
- smart_workspace_switcher
- quick_domains
- presentation
You can define your own extensions. If you believe an extension may be useful to others, then please submit a PR.
Custom extensions requires a show
event to be defined. When the show
event is triggered the defined sections
will be shown. If a section in sections
is not defined it will use the default from the config.
The hide
event is optional. When the hide
event is triggered the extension will hide the defined sections
.
If the hide
event is not defined the extension will hide the sections
after the delay
which is set to 5 seconds by default.
You can also have a delay
with the hide
event, which will delay the hide for the specified time in seconds after the hide
event is triggered.
You can also have a optional callback
function that will be called when the show
event is triggered with the properties from the event.
The colors
overwrite the default colors for the extension to use with its sections
.
local my_extension = {
'my_extension_name',
events = {
show = 'my_plugin.show',
hide = 'my_plugin.hide',
delay = 3
callback = function(window)
wezterm.log_info('Extension was shown')
end
},
sections = {
tabline_a = { 'mode' }
},
colors = {
a = { fg = '#181825', bg = '#f38ba8' },
b = { fg = '#f38ba8', bg = '#313244' },
c = { fg = '#cdd6f4', bg = '#181825' },
}
}
tabline.setup({ extensions = { my_extension } })
You can also pass multiple events to the show
and hide
properties.
events = {
show = { 'my_plugin.show', 'my_plugin.show2' },
hide = { 'my_plugin.hide', 'my_plugin.hide2' }
}
By default tabline refreshes itself based on the status_update_interval
. However you can also force
tabline to refresh at any time by calling tabline.refresh
function.
The refresh function needs the Window object to refresh the tabline, and the TabInformation object to refresh the tabs. If passing one of them as nil it won't refresh the respective section.
tabline.refresh(window, tab)
Avoid calling tabline.refresh
inside components. Since components are evaluated
during refresh, calling refresh while refreshing can have undesirable effects.
You can also disable tabline completely. By setting the enable_tab_bar option to false in the WezTerm config.
Thanks to MLFlexer for some tips in developing a plugin for WezTerm.
Thanks to lualine.nvim for the inspiration and a nice statusline for my Neovim.