Overview
guiltty is a GPU-accelerated terminal emulator in Elixir and C. Elixir holds the
behavior: the parser, the screen, tabs, windows, config. C does the syscalls and the
pixels: a PTY NIF, a renderer (Vulkan, or OpenGL) that runs as its own process, and
the small guiltty command.
It runs on Linux only. It's been tried on openSUSE Tumbleweed, X11 and an AMD GPU; Wayland, Intel, NVIDIA and other distributions should work but haven't been tried.
Requirements
guiltty is built from source on your machine, so installing it needs Elixir and a C toolchain. The installed copy carries its own Erlang runtime: once it's installed, it runs without Elixir.
| What | Why |
|---|---|
| Elixir 1.18+, Erlang/OTP 26+ | building guiltty and its release |
| GCC 15+, make, pkg-config |
the C is C23 and uses #embed
|
| SDL3 (development files) | the window, keyboard and mouse |
| FreeType, fontconfig, HarfBuzz (dev files) | finding fonts, drawing and shaping text |
| Vulkan loader and headers (dev files) | the Vulkan renderer |
| glslc (from shaderc) | compiles the Vulkan shaders to SPIR-V |
Your distribution's packages work if they're recent enough; for Elixir and Erlang, a
version manager such as asdf
gets you exact versions. elixir --version
should say Elixir 1.18 or newer, compiled with Erlang/OTP 26 or newer.
Tested Elixir and Erlang/OTP
| Elixir | Erlang/OTP | Status |
|---|---|---|
| 1.20.4 | 29 | Developed on |
| 1.19.7 | 28 | Builds, tests pass |
| 1.19.7 | 26 | Builds, tests pass |
| 1.18.4 | 26 | Builds, tests pass |
Anything older than Elixir 1.18 or Erlang/OTP 26 isn't supported.
GPU and fonts
guiltty needs a GPU driver with OpenGL 3.3, which almost any desktop from the last decade has. For Vulkan it needs a Vulkan 1.3 driver (for AMD and Intel GPUs, Mesa's; only AMD has been tried). Without one, it draws with OpenGL.
Fonts come from fontconfig. guiltty looks best with a Nerd Font (for prompt icons), Noto Color Emoji (for emoji) and a font for Chinese, Japanese and Korean, such as Noto Sans CJK.
Install
1. What to install first
On these distributions, one command installs it all. Only openSUSE has been tried; the other package names come from each distribution's package listings.
sudo zypper install gcc make pkg-config SDL3-devel freetype2-devel \
fontconfig-devel harfbuzz-devel vulkan-devel shaderc elixir erlang-epmd
The one guiltty is developed on.
sudo dnf install gcc make pkgconf-pkg-config SDL3-devel freetype-devel \
fontconfig-devel harfbuzz-devel vulkan-loader-devel vulkan-headers glslc elixir
If Fedora's Elixir is older than 1.18 (elixir --version), leave out elixir and get Elixir and Erlang from asdf.
sudo apt install build-essential pkg-config libsdl3-dev libfreetype-dev \
libfontconfig-dev libharfbuzz-dev libvulkan-dev glslc elixir erlang-dev
Older Ubuntus have neither SDL3 nor GCC 15.
sudo pacman -S --needed base-devel sdl3 freetype2 fontconfig harfbuzz \
vulkan-icd-loader vulkan-headers shaderc elixir
Everything is in the official repositories.
2. guiltty
git clone https://github.com/z7ealth/guiltty.git
cd guiltty
./install.sh
install.sh
checks for the tools above, builds a release and installs it into ~/.local; it needs no root.
| Where | What |
|---|---|
~/.local/lib/guiltty/
|
the release |
~/.local/bin/guiltty
|
the command |
~/.local/share/applications/guiltty.desktop
|
the menu entry |
~/.local/share/icons/hicolor/*/apps/guiltty.png
|
the icon |
3. Run it
Run guiltty, or find it in your applications menu. If your shell says guiltty: command not found, add
~/.local/bin
to your PATH.
Update and uninstall
- Update
-
shell Copy
git pull && ./install.shIf guiltty is running, the script asks before stopping it, since that closes its tabs (
--yesskips the question). Run from a guiltty tab, it finishes after that tab closes, then opens guiltty again; its output goes to~/.local/state/guiltty/install.log. - Uninstall
-
shell Copy
./install.sh --uninstallYour config stays.
- Install somewhere else
-
shell Copy
PREFIX=/opt/guiltty ./install.sh
Windows and tabs
Every window has its own tabs. guiltty
(or
Ctrl
+
Shift
+
N
) opens another window, and closing one never
touches the others. guiltty stops after its last window.
Locked tabs
Closing a window ends its tabs, except the ones you lock with
Ctrl
+
Shift
+
L
. They show a 🔒 and keep running, and the next
guiltty
brings that window back with them, where you left them.
Closing a busy tab
Ctrl + Shift + W on a tab that's running a program (vim, ssh, a build) asks first: the tab bar says what's running, and pressing it again closes the tab. A tab waiting at its prompt closes at once.
Marks
A tab you're not looking at shows ● when it prints something and 🔔 when it rings
the bell (a build that ends with ; printf '\a', say), until you switch
to it.
Names and order
Ctrl + Shift + R renames the tab: type a name, Enter. The name stays even when the program changes its title; an empty name gives the title back. Ctrl + Shift + PgUp and Ctrl + Shift + PgDn move the tab left and right.
Where a new tab starts
A new window's first tab starts in the folder you ran guiltty
in. Other new tabs follow working_directory: by default, where the
current tab's shell is. A tab's shell gets its window's display (DISPLAY
and the like), so GUI programs started from it open where that window is.
Scrollback, search, selection
Each tab keeps 10,000 rows of scrollback by default (scrollback).
Shift
+
PgUp
and
Shift
+
PgDn
scroll back and forward, as does the mouse wheel;
Shift
+
Home
and
Shift
+
End
jump to the oldest row and back to the prompt.
Ctrl
+
Shift
+
K
clears the scrollback; the screen stays.
Search
Ctrl + Shift + F searches the scrollback. Enter finds the next older match, Shift+Enter the next newer one, and Escape ends the search.
Selection and the clipboard
- Selecting with the mouse copies to the primary selection; a middle click pastes it.
- Ctrl + Shift + C copies the selection to the clipboard, and Ctrl + Shift + V or Shift + Insert paste it.
- In programs that use the mouse (vim, htop), hold Shift to select.
-
copy_on_selectchanges what selecting does::clipboardalso copies to the clipboard,falsecopies nothing until Ctrl + Shift + C .
Broadcast
Broadcast types into every tab in a group at once: the same change on five servers,
say. Open a tab per server, ssh
in each, and press
Ctrl
+
Shift
+
B
in each to put it in the group.
What you type in one goes to all of them. Each window has its own group.
-
A tab can join only while it's running one of
broadcast_programs(by defaultsshandmosh-client). - If a connection drops, typing goes nowhere until that tab leaves the group, and the tab bar shows which tab was lost. A command meant for your servers doesn't land on your own machine.
- A local tab (your own shell) joins on purpose with a second Ctrl + Shift + B . It's marked 🏠 and isn't checked.
Keys
These are the defaults, as they ship. Every one can be rebound in the config's keys, and
Ctrl
+
Shift
+
/
shows them as you bound them.
new_window
A new window, with its own tabs.
Ctrl
+
Shift
+
N
new_tab
A new tab.
Ctrl
+
Shift
+
T
close_tab
Close the tab. If it's running a program, the tab bar says what, and pressing it again closes it.
Ctrl
+
Shift
+
W
next_tab
The next tab.
Ctrl
+
PgDn
Ctrl
+
Tab
prev_tab
The previous tab.
Ctrl
+
PgUp
Ctrl
+
Shift
+
Tab
move_tab_left
Move the tab left.
Ctrl
+
Shift
+
PgUp
move_tab_right
Move the tab right.
Ctrl
+
Shift
+
PgDn
rename_tab
Rename the tab: type a name, Enter. An empty name gives the program's title back.
Ctrl
+
Shift
+
R
scroll_page_up
Scroll back a page.
Shift
+
PgUp
scroll_page_down
Scroll forward a page.
Shift
+
PgDn
scroll_top
Scroll to the oldest row.
Shift
+
Home
scroll_bottom
Scroll back to the prompt.
Shift
+
End
clear_scrollback
Drop the tab's scrollback; the screen stays.
Ctrl
+
Shift
+
K
search
Search the scrollback: Enter finds older, Shift+Enter newer, Escape ends.
Ctrl
+
Shift
+
F
copy
Copy the selection to the clipboard.
Ctrl
+
Shift
+
C
zoom_in
Bigger text.
Ctrl
+
=
Ctrl
+
Shift
+
=
zoom_out
Smaller text.
Ctrl
+
-
zoom_reset
The configured size again.
Ctrl
+
0
open_config
Open the config file in your editor, creating it from the example first.
Ctrl
+
,
cheatsheet
Every key, as you bound it, on one page.
Ctrl
+
Shift
+
/
lock_tab
Lock the tab, so it outlives its window.
Ctrl
+
Shift
+
L
broadcast
Put the tab in the broadcast group, or take it out.
Ctrl
+
Shift
+
B
| Action | Keys | What it does |
|---|---|---|
new_window
|
Ctrl + Shift + N | A new window, with its own tabs. |
new_tab
|
Ctrl + Shift + T | A new tab. |
close_tab
|
Ctrl + Shift + W | Close the tab. If it's running a program, the tab bar says what, and pressing it again closes it. |
next_tab
|
Ctrl + PgDn Ctrl + Tab | The next tab. |
prev_tab
|
Ctrl + PgUp Ctrl + Shift + Tab | The previous tab. |
move_tab_left
|
Ctrl + Shift + PgUp | Move the tab left. |
move_tab_right
|
Ctrl + Shift + PgDn | Move the tab right. |
rename_tab
|
Ctrl + Shift + R | Rename the tab: type a name, Enter. An empty name gives the program's title back. |
scroll_page_up
|
Shift + PgUp | Scroll back a page. |
scroll_page_down
|
Shift + PgDn | Scroll forward a page. |
scroll_top
|
Shift + Home | Scroll to the oldest row. |
scroll_bottom
|
Shift + End | Scroll back to the prompt. |
clear_scrollback
|
Ctrl + Shift + K | Drop the tab's scrollback; the screen stays. |
search
|
Ctrl + Shift + F | Search the scrollback: Enter finds older, Shift+Enter newer, Escape ends. |
copy
|
Ctrl + Shift + C | Copy the selection to the clipboard. |
zoom_in
|
Ctrl + = Ctrl + Shift + = | Bigger text. |
zoom_out
|
Ctrl + - | Smaller text. |
zoom_reset
|
Ctrl + 0 | The configured size again. |
open_config
|
Ctrl + , | Open the config file in your editor, creating it from the example first. |
cheatsheet
|
Ctrl + Shift + / | Every key, as you bound it, on one page. |
lock_tab
|
Ctrl + Shift + L | Lock the tab, so it outlives its window. |
broadcast
|
Ctrl + Shift + B | Put the tab in the broadcast group, or take it out. |
Fixed keys
Paste (
Ctrl
+
Shift
+
V
,
Shift
+
Insert
), middle-click
paste and Shift-to-select aren't in keys and can't be rebound.
Rebinding
Each action takes a list of keys. Names are letters, digits, f1
to f12, tab, enter, page_up, page_down, home, end, insert, delete, or a punctuation character like
=
or -, with any of the ctrl+, shift+
and alt+
modifiers. Actions you leave out keep their defaults.
keys: [
new_tab: ["ctrl+t"],
search: ["ctrl+shift+f", "f3"]
]
The config file
The config file is ~/.config/guiltty/config.exs
(or the path in GUILTTY_CONFIG).
Ctrl
+
,
creates it from the example config, which documents every setting, and opens it in
your editor.
It's plain Elixir: the last expression is the config, a keyword list. Settings you leave out keep their defaults.
- guiltty checks the file every second and applies a saved change to the open windows. A few settings apply only to what opens next; the settings table says which.
-
If the file doesn't evaluate, or a setting is wrong, the previous config stays.
The tab bar shows the error in red, with the line
(
config.exs:4: theme: unknown name ...), and a misspelled setting gets a suggestion.
Settings
font
family: any fontconfig name; size: 6 to 96
[family: "monospace", size: 18]
theme
a built-in name, or your own colors. See Themes.
"tomorrow-night"
cursor
style: :block, :bar or :underline. Programs can change the style while they run (vim shows a bar in insert mode); it comes back when they're done.
[style: :block, blink: false]
window: padding
0 to 100. Pixels around the text; more on a HiDPI screen.
6
window: columns, rows
10 to 1000, 3 to 1000. The size of a new window, in cells.
80, 24
window: opacity
0.1 to 1.0. The background's. Needs a compositor; applies to windows opened after a change.
1.0
shell
[command: "/usr/bin/zsh", args: ["-l"]]. The program each new tab runs.
your login shell
working_directory
:inherit, :home or a path. :inherit starts a new tab where the current tab's shell is.
:inherit
copy_on_select
true, :clipboard or false. true copies a selection to the primary selection; :clipboard to the clipboard too; false copies nothing until Ctrl+Shift+C.
true
wheel_lines
1 to 50. Rows the mouse wheel scrolls per step.
3
bold_is_bright
true or false. Bold text in the 8 basic colors uses their bright versions, as xterm does.
true
broadcast_programs
a list of program names. What a tab must be running to join the broadcast group.
["ssh", "mosh-client"]
tab_bar
:bottom or :top
:bottom
tab_style
:flat, :powerline, :rounded, :minimal or :separators. How the tabs look. Their colors are in the theme.
:flat
ligatures
true or false. Join ->, !=, |> and the like into one symbol, when the font has ligatures.
true
renderer
:vulkan or :gl. Applies to windows opened after a change. GUILTTY_RENDERER wins over it.
:vulkan
scrollback
0 to 1,000,000. Rows each tab keeps for scrolling back; applies to new tabs.
10_000
keys
action: [key, ...]. Any action you leave out keeps its default.
see Keys
| Setting | Default | Accepts |
|---|---|---|
font
|
[family: "monospace", size: 18]
|
family: any fontconfig name; size: 6 to 96 |
theme
|
"tomorrow-night"
|
a built-in name, or your own colors See Themes. |
cursor
|
[style: :block, blink: false]
|
style: :block, :bar or :underline Programs can change the style while they run (vim shows a bar in insert mode); it comes back when they're done. |
window: padding
|
6
|
0 to 100 Pixels around the text; more on a HiDPI screen. |
window: columns, rows
|
80, 24
|
10 to 1000, 3 to 1000 The size of a new window, in cells. |
window: opacity
|
1.0
|
0.1 to 1.0 The background's. Needs a compositor; applies to windows opened after a change. |
shell
|
your login shell | [command: "/usr/bin/zsh", args: ["-l"]] The program each new tab runs. |
working_directory
|
:inherit
|
:inherit, :home or a path :inherit starts a new tab where the current tab's shell is. |
copy_on_select
|
true
|
true, :clipboard or false true copies a selection to the primary selection; :clipboard to the clipboard too; false copies nothing until Ctrl+Shift+C. |
wheel_lines
|
3
|
1 to 50 Rows the mouse wheel scrolls per step. |
bold_is_bright
|
true
|
true or false Bold text in the 8 basic colors uses their bright versions, as xterm does. |
broadcast_programs
|
["ssh", "mosh-client"]
|
a list of program names What a tab must be running to join the broadcast group. |
tab_bar
|
:bottom
|
:bottom or :top |
tab_style
|
:flat
|
:flat, :powerline, :rounded, :minimal or :separators How the tabs look. Their colors are in the theme. |
ligatures
|
true
|
true or false Join ->, !=, |> and the like into one symbol, when the font has ligatures. |
renderer
|
:vulkan
|
:vulkan or :gl Applies to windows opened after a change. GUILTTY_RENDERER wins over it. |
scrollback
|
10_000
|
0 to 1,000,000 Rows each tab keeps for scrolling back; applies to new tabs. |
keys
|
see Keys | action: [key, ...] Any action you leave out keeps its default. |
Themes
Pick a built-in theme by name:
-
tomorrow-nightTomorrow Night (the default) -
catppuccin-mochaCatppuccin Mocha -
draculaDracula -
gruvbox-darkGruvbox Dark -
solarized-darkSolarized Dark
Or bring your own colors. Any you leave out come from name:
(or from tomorrow-night without one), so you can start from a theme and change
single colors.
theme: [
name: "catppuccin-mocha",
fg: "#c5c8c6",
bg: "#1d1f21",
cursor: "#c5c8c6",
# black, red, green, yellow, blue, magenta, cyan, white, then bright
ansi: ~w(#1d1f21 #cc6666 #b5bd68 #f0c674 #81a2be #b294bb #8abeb7 #c5c8c6
#969896 #d54e53 #b9ca4a #e7c547 #7aa6da #c397d8 #70c0b1 #eaeaea),
# The tab bar's colors. Any you leave out are worked out from the
# theme, to suit the tab_style.
tab_bar: "#282a36",
tab_text: "#6272a4",
tab_active: "#ff79c6",
tab_active_text: "#282a36",
tab_accent: "#ff79c6"
]
Tab bar colors
| Color | What it colors |
|---|---|
tab_bar
|
the bar behind the tabs |
tab_text
|
the other tabs' titles |
tab_active
|
the active tab's background |
tab_active_text
|
the active tab's title |
tab_accent
|
the line along the active tab (flat, minimal), or the filled tab's color (the other styles) |
Any you leave out are worked out from the theme, to suit the tab style.
Tab styles
tab_style
picks how the tab bar is drawn. The previews use Catppuccin Mocha; guiltty works
out each style's colors from your theme, unless you set them.
:flat Side by side, the active one lighter, with a line along its edge. The default.
:powerline Colored blocks joined by arrows.
:rounded Pills with round ends, the active one filled.
:minimal No bar: titles on the terminal's background, the line under the active one.
:separators Titles between thin bars, the active one filled.
Fonts and ligatures
font
takes a fontconfig family name and a size from 6 to 96: font: [family: "JetBrains Mono", size: 14]. Characters the font doesn't
have come from fallback fonts, so emoji, Nerd Font icons and CJK text show if those
fonts are installed.
With a font that has ligatures (JetBrains Mono and Fira Code do; Source Code Pro
doesn't), ->, !=, |>, =>
and the like are drawn as one symbol. ligatures: false
turns that off.
Powerline arrows and half circles in prompts (U+E0B0, U+E0B2, U+E0B4, U+E0B6) are drawn by guiltty itself, like box drawing: they meet the blocks next to them exactly, and show even without a Nerd Font.
Vulkan or OpenGL
guiltty draws with Vulkan 1.3 by default. If Vulkan can't start (an old GPU, no driver), it switches to OpenGL by itself and says so in the tab bar. Both draw the same picture: a test replays recorded frames through each and compares them pixel for pixel.
-
renderer: :glalways uses OpenGL, starting with the next window, so one window can be Vulkan and the next OpenGL. -
GUILTTY_RENDERER=gl(orvulkan) does the same for a whole run, whatever the config says. It's read when guiltty starts:guiltty stop, thenGUILTTY_RENDERER=gl guiltty.
Example config
The example config as it ships, with every setting at its default except the theme.
[
font: [family: "monospace", size: 18],
# A built-in theme by name: "catppuccin-mocha", "tomorrow-night" (what
# guiltty uses without a config), "dracula", "gruvbox-dark" or
# "solarized-dark".
theme: "catppuccin-mocha",
# :block, :bar or :underline. Programs can change it while they run (vim
# shows a bar in insert mode); it comes back when they're done.
cursor: [style: :block, blink: false],
window: [
# Pixels around the text (more on a HiDPI screen).
padding: 6,
# The size of a new window, in cells.
columns: 80,
rows: 24,
# The background's opacity, 0.1 to 1.0. Needs a compositor; applies to
# windows opened after a change.
opacity: 1.0
],
# The program each new tab runs. Without a command: your login shell.
# shell: [command: "/usr/bin/zsh", args: ["-l"]],
# Where a new tab starts: :inherit (where the current tab's shell is),
# :home, or a path.
working_directory: :inherit,
# Selecting text copies it to the primary selection (middle click pastes
# it); :clipboard copies it to the clipboard too; false copies nothing
# until Ctrl+Shift+C.
copy_on_select: true,
# Rows the mouse wheel scrolls per step.
wheel_lines: 3,
# Bold text in the 8 basic colors uses their bright versions, as xterm
# does.
bold_is_bright: true,
# Broadcast (Ctrl+Shift+B): a tab may join only while it runs one of
# these. If one of them stops (a dropped connection), typing goes nowhere
# until that tab leaves the group. A local tab (your own shell) joins on
# a second Ctrl+Shift+B, and isn't checked.
broadcast_programs: ["ssh", "mosh-client"],
# Where the tabs go: :bottom (the default) or :top.
tab_bar: :bottom,
# How the tabs look:
# :flat (the default) side by side, the active one lighter, with
# a line along its edge
# :powerline colored blocks joined by arrows
# :rounded pills with round ends, the active one filled
# :minimal no bar: titles on the terminal's background, the line
# under the active one
# :separators titles between thin bars, the active one filled
# Their colors are in theme: (tab_bar, tab_active, ... above).
tab_style: :flat,
# Join ->, !=, |>, => and the like into one symbol, when the font has
# ligatures (JetBrains Mono and Fira Code do; Source Code Pro doesn't).
ligatures: true,
# How guiltty draws: :vulkan, or :gl (OpenGL). If Vulkan can't start
# (no driver, no GPU that can show the window), guiltty says so and
# draws with OpenGL. Applies to windows opened after a change.
# GUILTTY_RENDERER=gl (or vulkan) in guiltty's environment wins over
# this, to try the other one for a run: `guiltty stop`, then
# `GUILTTY_RENDERER=gl guiltty`.
renderer: :vulkan,
# Rows of output each tab keeps for scrolling back (new tabs only).
scrollback: 10_000,
# Each action takes a list of keys. Names: letters, digits, f1-f12,
# tab, enter, page_up, page_down, home, end, insert, delete, or a
# punctuation character like = or -, with any of the ctrl+, shift+ and
# alt+ modifiers.
keys: [
new_window: ["ctrl+shift+n"],
new_tab: ["ctrl+shift+t"],
close_tab: ["ctrl+shift+w"],
next_tab: ["ctrl+page_down", "ctrl+tab"],
prev_tab: ["ctrl+page_up", "ctrl+shift+tab"],
move_tab_left: ["ctrl+shift+page_up"],
move_tab_right: ["ctrl+shift+page_down"],
rename_tab: ["ctrl+shift+r"],
scroll_page_up: ["shift+page_up"],
scroll_page_down: ["shift+page_down"],
scroll_top: ["shift+home"],
scroll_bottom: ["shift+end"],
clear_scrollback: ["ctrl+shift+k"],
search: ["ctrl+shift+f"],
copy: ["ctrl+shift+c"],
zoom_in: ["ctrl+=", "ctrl+shift+="],
zoom_out: ["ctrl+-"],
zoom_reset: ["ctrl+0"],
open_config: ["ctrl+,"],
cheatsheet: ["ctrl+shift+/"],
lock_tab: ["ctrl+shift+l"],
broadcast: ["ctrl+shift+b"]
]
]
The guiltty command
guiltty
is a small program that asks the running guiltty for a window over a private socket,
like emacsclient. If guiltty isn't running, it starts it.
| Command | What it does |
|---|---|
guiltty
|
Opens a window, or brings back a closed one that kept locked tabs. Its first tab starts in the folder you ran it in. |
guiltty stop
|
Stops guiltty: every window and every tab, locked ones too. |
guiltty pid
|
Prints the running guiltty's process id. |
guiltty remote
|
An IEx shell inside the running guiltty. |
guiltty's Erlang node listens on 127.0.0.1 only, is named guiltty@127.0.0.1, and takes its cookie from ~/.erlang.cookie.
install.sh
stops if that file can be read by other users.
Environment variables
| Variable | What it does |
|---|---|
GUILTTY_CONFIG
|
The config file to use instead of ~/.config/guiltty/config.exs.
|
GUILTTY_RENDERER
|
gl
or vulkan, for a whole run, whatever the config says. Read when
guiltty starts.
|
PREFIX
|
For install.sh: where to install instead of ~/.local.
|
Troubleshooting
guiltty: command not found-
Add
~/.local/binto yourPATH. - The tab bar says guiltty is drawing with OpenGL
-
Vulkan couldn't start: there's no Vulkan 1.3 driver, or no GPU that can show the window. guiltty works the same with OpenGL. To stop it trying Vulkan, set
renderer: :gl. - The tab bar shows an error in red
-
The config file has a mistake, at the line it names. The previous config stays until you fix it and save.
- A setting didn't change anything
-
window: opacityandrendererapply to windows opened after the change, andscrollbackto new tabs. Opacity also needs a compositor. - Emoji, icons or CJK text show as boxes
-
Install fonts that have them: a Nerd Font, Noto Color Emoji and Noto Sans CJK.
- I can't select text in vim or htop
-
They use the mouse; hold Shift to select.
- Updating from a guiltty tab
-
The tab closes when the old guiltty stops; the install carries on and opens guiltty again. Its output is in
~/.local/state/guiltty/install.log.
Building from source
To work on guiltty itself, from a clone:
mix deps.get\nmix guiltty # a window from the source tree\nmix test\niex -S mix # the same, with an iex shell alongside
mix compile
also builds the C: the NIF and both renderers, each in a normal and a debug build.
make bench
measures throughput, and AGENTS.md
lists every other command.
Learn more
- The book explains every part, with diagrams, from pseudoterminals to tab styles.
- CHANGELOG.md lists what each version added.
- DEVLOG.md is what happened along the way.
-
guiltty is under the MIT License. The
installed copy also carries the Erlang runtime and the
telemetrylibrary, both under the Apache License 2.0.