No description
  • Odin 78.5%
  • Python 19.8%
  • Nix 1.3%
  • Shell 0.3%
Find a file
Antoine Vaure 9d25c1fd0e
All checks were successful
ci-tron/ci Success
test: optional live input test (wheel, mouse encodings, prompt jumping)
Runs term in an isolated headless sway and delivers real Wayland events: a
persistent zwlr_virtual_pointer_v1 client (pywayland, bindings generated from
the protocol XML at run time) for wheel and clicks, and wtype for keys. Checks
alt scroll, the four mouse encodings and Ctrl+Shift+Z/X prompt jumps by what
the program inside receives and by screenshot pixels.

Lives in its own dev shell (nix develop .#live-input) and is not part of CI or
the build.
2026-10-29 15:55:37 +01:00
dbus Add light/dark theme switch, following system preference by default 2026-09-14 20:06:42 +02:00
docs test: optional live input test (wheel, mouse encodings, prompt jumping) 2026-10-29 15:55:37 +01:00
font feat: support multiple configurable fallback fonts 2026-09-22 22:52:11 +02:00
fontconfig Add fontconfig-based font discovery and a fallback face 2026-09-16 15:25:47 +02:00
harfbuzz perf: cache HarfBuzz shaped-run output across frames 2026-09-22 19:02:40 +02:00
issues docs: file issues for the pinned terminfo.dev suite and the calibration race 2026-10-29 15:23:54 +01:00
nix feat: select separate light and dark Kitty themes 2026-10-26 22:24:09 +01:00
profile feat: add pty logging, replay and fixture tests 2026-10-02 11:41:20 +02:00
pty fix: report PTY pixel dimensions to image clients 2026-10-26 10:10:33 +01:00
render feat(render): dim the background of the hovered link 2026-10-29 14:24:36 +01:00
scripts test: optional live input test (wheel, mouse encodings, prompt jumping) 2026-10-29 15:55:37 +01:00
testdata test(input): exercise Neovim mappings across tmux and SSH contexts 2026-10-25 22:20:56 +01:00
vt feat: OSC 133/633 prompt marks and jump between prompts 2026-10-29 15:22:53 +01:00
wayland feat: default arrow cursor, hand over OSC 8 links (wp_cursor_shape_v1) 2026-10-07 13:52:10 +02:00
xkb test: retain keyboard SSH diagnostics and pin XKB alias fixtures 2026-10-25 23:18:19 +01:00
.ci-tron.json test(input): exercise Neovim mappings across tmux and SSH contexts 2026-10-25 22:20:56 +01:00
.gitignore test: validate Kitty graphics with real clients 2026-10-26 10:21:55 +01:00
AGENTS.md docs: move ssh, tmux and debugging into docs/tmux-and-ssh.md, add docs/development.md 2026-10-20 19:33:38 +02:00
buffers.odin fix: stop leaking wl listener structs and the shm mmap on every frame/resize 2026-09-16 15:26:32 +02:00
CLAUDE.md Add project docs: README, ROADMAP, AGENTS conventions 2026-09-09 13:23:05 +02:00
cli.odin feat: add pty logging, replay and fixture tests 2026-10-02 11:41:20 +02:00
cli_test.odin feat: add pty logging, replay and fixture tests 2026-10-02 11:41:20 +02:00
clipboard.odin feat: keep mouse selection on its text while scrolling 2026-10-29 10:52:32 +01:00
clipboard_test.odin feat: make OSC 52 clipboard reads configurable and measure them in the probe 2026-10-02 17:16:02 +02:00
config.odin feat: OSC 133/633 prompt marks and jump between prompts 2026-10-29 15:22:53 +01:00
e2e_features_test.odin feat: verify completed capabilities and forward OSC 7 through tmux policy 2026-10-25 23:35:50 +01:00
e2e_keyboard_test.odin test(input): exercise Neovim mappings across tmux and SSH contexts 2026-10-25 22:20:56 +01:00
e2e_test.odin test: wait for shell setup before disabling restored tty echo 2026-10-25 23:55:48 +01:00
e2e_tmux_test.odin test(input): exercise Neovim mappings across tmux and SSH contexts 2026-10-25 22:20:56 +01:00
events.odin fix(pty): bound and asynchronously flush terminal input and replies 2026-10-25 23:57:46 +01:00
flake.lock Add Nix flake for building and developing term 2026-09-09 13:22:58 +02:00
flake.nix test: optional live input test (wheel, mouse encodings, prompt jumping) 2026-10-29 15:55:37 +01:00
input.odin feat: OSC 133/633 prompt marks and jump between prompts 2026-10-29 15:22:53 +01:00
input_test.odin feat: UTF-8, urxvt and pixel mouse encodings (?1005, ?1015, ?1016) 2026-10-29 15:18:17 +01:00
kitty.odin fix: omit Kitty text-key lock bits without report-all 2026-10-26 09:58:30 +01:00
kitty_media.odin fix: make Kitty local media an explicit opt-in 2026-10-26 10:01:18 +01:00
kitty_media_test.odin test: validate Kitty graphics with real clients 2026-10-26 10:21:55 +01:00
kitty_test.odin test: validate Kitty graphics with real clients 2026-10-26 10:21:55 +01:00
links.odin links: skip truncated URLs, join hard-wrapped rows, let OSC 8 cells shadow detection 2026-10-20 20:32:24 +02:00
links_test.odin links: skip truncated URLs, join hard-wrapped rows, let OSC 8 cells shadow detection 2026-10-20 20:32:24 +02:00
main.odin feat: keep mouse selection on its text when applications scroll 2026-10-29 10:58:43 +01:00
main_test.odin feat: OSC 133/633 prompt marks and jump between prompts 2026-10-29 15:22:53 +01:00
modes_test.odin feat: implement focus reporting and synchronized output modes 2026-10-02 11:52:31 +02:00
open_url.odin feat: implement OSC 8 hyperlinks with ctrl+click-to-open 2026-09-16 15:27:44 +02:00
osc_color.odin Implement ordered dynamic 256-colour palette operations 2026-10-25 23:25:09 +01:00
osc_color_test.odin fix(palette): accept bulk updates and preserve reset under overload 2026-10-25 23:52:01 +01:00
pty_output.odin fix(pty): bound and asynchronously flush terminal input and replies 2026-10-25 23:57:46 +01:00
pty_output_test.odin fix(pty): bound and asynchronously flush terminal input and replies 2026-10-25 23:57:46 +01:00
ptylog.odin fix(pty): bound and asynchronously flush terminal input and replies 2026-10-25 23:57:46 +01:00
ptylog_test.odin feat: answer XTVERSION, XTGETTCAP, DECRQSS and pixel-size queries 2026-10-02 11:49:35 +02:00
ptyreplay.odin feat: answer colour-scheme queries and send scheme change notices (?2031, CSI ? 996 n) 2026-10-02 18:55:18 +02:00
README.md feat: OSC 133/633 prompt marks and jump between prompts 2026-10-29 15:22:53 +01:00
ROADMAP.md docs: file issues for the pinned terminfo.dev suite and the calibration race 2026-10-29 15:23:54 +01:00
select.odin feat: keep mouse selection on its text when applications scroll 2026-10-29 10:58:43 +01:00
select_test.odin feat: keep mouse selection on its text when applications scroll 2026-10-29 10:58:43 +01:00
terminfo.odin Implement ordered dynamic 256-colour palette operations 2026-10-25 23:25:09 +01:00
terminfo_test.odin feat: UTF-8, urxvt and pixel mouse encodings (?1005, ?1015, ?1016) 2026-10-29 15:18:17 +01:00
theme.odin feat: select separate light and dark Kitty themes 2026-10-26 22:24:09 +01:00
theme_test.odin feat: select separate light and dark Kitty themes 2026-10-26 22:24:09 +01:00
working_directory.odin feat: retain OSC 7 working-directory reports and declare Swd 2026-10-25 23:23:19 +01:00
working_directory_test.odin feat: retain OSC 7 working-directory reports and declare Swd 2026-10-25 23:23:19 +01:00

term

A fast, small, hackable terminal emulator for Wayland.
Written in Odin, built with Nix, configured by editing one file.

term with a dark theme term with a light theme

Why term

  • Modern protocols, done properly. Kitty keyboard protocol, the complete kitty graphics protocol (placements, animation, Unicode placeholders), sixel, synchronized output, grapheme clustering, OSC 8 hyperlinks, OSC 52 clipboard.
  • Looks right. HarfBuzz shaping with ligatures, fontconfig family lookup and optional per-glyph fallback fonts, wide and CJK characters (give it a fallback font that has them), curly / dotted / dashed / coloured underlines, overline, a redefinable 256-colour palette, built-in box-drawing glyphs, scrollback that reflows when you resize.
  • Themes that follow your desktop. Light and dark switching through the desktop portal, and any kitty theme works as-is.
  • Plays well with tmux and ssh. term generates its own terminfo entry, ships a tmux policy file and an ssh wrapper, and a test suite compares its behaviour in 12 setups.
  • Keyboard-first selection. A vim-style select mode copies and opens links without the mouse.
  • Small and stateless. The whole project stays under 15 000 lines; configuration is an Odin value compiled in, in the spirit of dwm and st, so nothing is parsed, watched or out of sync with what is running.

Install

You need a Wayland compositor and Nix with flakes enabled.

nix run git+https://git.antoinevaure.fr/ant/term.git       # try it

From a checkout:

nix build && ./result/bin/term

The package also compiles and installs the term-256color terminfo entry.

Home Manager

{
  inputs.term.url = "git+https://git.antoinevaure.fr/ant/term.git";
  # ...
  imports = [ inputs.term.homeManagerModules.default ];

  programs.term = {
    enable = true;
    fontFamily = "Fira Code";           # fontconfig family, default "monospace"
    fontSize = 15;                      # pixels, default 16
    fallbackFontFamilies = [ "Symbols Nerd Font Mono" ];
    themeMode = "Auto";                 # "Auto" | "Dark" | "Light"
    kittyTheme = {                      # optional: kitty theme names per mode
      light = "Github";
      dark = "Dracula";
    };
  };
}

extraKeybinds and extraSelectKeybinds take raw Odin for a complete replacement of the key tables. If you used scripts/install_terminfo.py before, remove its copy once, so Home Manager can link the packaged one: rm ~/.terminfo/t/term-256color.

From source

nix develop
odin build . -out:term
scripts/install_terminfo.py     # once: installs the terminfo entry into ~/.terminfo

More in docs/development.md.

Using term

Keys

Keys Action
Ctrl+Shift+C / Ctrl+Shift+V Copy the selection / paste the clipboard
Ctrl+Shift+Space Enter keyboard select mode
Ctrl+Shift+? / Ctrl+Shift+. Increase / decrease the font size (6–96 px)¹
Shift+PageUp / Shift+PageDown Scroll back / forward a page
Ctrl+Shift+Z / Ctrl+Shift+X Jump to the previous / next shell prompt (needs shell integration marks, OSC 133 or 633)
Mouse wheel Scroll (3 lines per notch), or forwarded to apps that track the mouse
Drag, double-click, triple-click Select text, a word, a line (also sets the primary selection)
Hold after a double or triple click, then drag Extend the selection by whole words or lines
Ctrl+drag or Ctrl+Shift+drag Select a rectangle
Middle-click Paste the primary selection
Ctrl+click on a link Open it, even inside tmux or vim
Shift + mouse Select even when an app has captured the mouse

¹ These match the character your layout produces with Shift held, not a physical key, so they sit on different keys on different layouts; edit the table in config.odin if yours is awkward.

Select mode (Ctrl+Shift+Space) works on the scrollback and on full-screen apps alike. Other keys are swallowed until you leave.

Keys Action
h j k l Move
w b e / W B E Word motions (small / big word)
0 $ Start / end of line
g G Oldest scrollback line / newest line
Ctrl+U Ctrl+D Half page up / down
v V Ctrl+V Select by character / line / block (press again to cancel)
y Copy the selection to the clipboard
o Open the link under the cursor, or the first one in the selection
Esc Clear the selection, then leave select mode

OSC 8 hyperlinks and plain URLs (http://, https://, mailto:, file://, and a bare www.) are underlined under the pointer, which turns into a hand; Ctrl+click opens them with xdg-open. url_detection in config.odin limits detection to Schemes or turns it Off. A link the application declares (OSC 8) always wins over a detected one. Detected URLs follow soft and hard line wraps (tmux, pagers), stop at quotes, brackets and borders, and are ignored when cut short with ….

Command line

term [--class APP_ID] [--working-directory DIR] [--log-pty FILE] [-e COMMAND [ARGS...]]
term --version | --print-terminfo | --replay FILE
Flag Meaning
-e cmd args… Run a command instead of $SHELL
--class APP_ID Wayland app id, for window rules (default term)
--working-directory DIR Start in a directory
--log-pty FILE Record what apps send and what term answers (never your keystrokes)
--replay FILE Replay a recording headless and summarise it
--print-terminfo Print the terminfo source term generates
--version Print the commit it was built from

Configuration

Settings live in config.odin, one Odin value that you edit and rebuild with nix build or odin build . (with Home Manager, the options above set the common ones for you). The ones people reach for:

Field Default
font_family "monospace" Fontconfig family; bold and italic faces are found for you
fallback_font_families none Consulted per glyph, in order, when the main font lacks it
font_size 16 Pixels; step and range are font_size_step, font_size_min, font_size_max
theme_mode Auto Auto follows the desktop's light/dark setting; or force Dark / Light
theme_dark_overrides, theme_light_overrides none Replace individual palette entries per mode
scrollback_capacity 2000 Rows kept on the main screen
default_cursor_shape Block Block, Underline, Bar; apps can change it
block_select_mods Ctrl, Ctrl+Shift Modifier combinations that make a drag select a rectangle
url_detection Schemes_And_Www Schemes, or Off for OSC 8 only
osc52_read true Let programs read the clipboard; false answers with an empty reply
kitty_local_media true Let graphics clients name files and shared memory; see the issue
keybinds, select_keybinds see above The key tables themselves

Colours

The palette can also come from a file, read at startup and again on SIGUSR1: $XDG_CONFIG_HOME/term/theme (else ~/.config/term/theme). It overrides foreground, background and color0…color15. Both color1=ff5555 and kitty's color1 #ff5555 lines work, so a kitty theme can be copied or symlinked there unchanged, and a missing file changes nothing. Home Manager's kittyTheme.light and kittyTheme.dark select separate Kitty palettes, stored in term/theme-light and term/theme-dark beside the shared file. themeMode selects which applies (Auto follows the desktop); each defaults to null, keeping that mode's built-in palette. These files are also read at startup and on SIGUSR1. The shared term/theme file overrides either mode's palette; nix/noctalia-theme.tmpl is a template for generating it from the Noctalia shell's colours. The previous string-valued kittyTheme = "Name" option is replaced by this light/dark attribute set.

Browse themes live with the picker, inside term:

nix run .#theme-preview          # arrows to move, type to filter,
                                 # Enter keeps it for this window, Esc restores
nix run .#theme-preview -- --list --dark

It uses the standard colour sequences any program may send (OSC 4 for entries 0–15, OSC 10 and 11 to set, OSC 104, 110 and 111 to reset). Those change only that window, survive a theme-file reload, and are not cleared by a terminal reset.

tmux, ssh and other hosts

term-256color is an entry only your machine has. For ssh there is a wrapper that installs it on first login (or falls back to xterm-256color), and tmux needs one source-file line to pass Shift+Enter and friends through. Everything else, plus how to debug a misbehaving setup, is in docs/tmux-and-ssh.md.

Every detectable feature, with whether term implements it and how it looks inside tmux and over ssh, is tabulated in docs/capabilities.md.

Not there yet

No bell or desktop notifications, HiDPI / fractional scaling, input methods (IME), scrollback search, tabs or splits, client-side decorations, overline, working-directory reports (OSC 7) and colour emoji. Software rendering only. The kitty graphics protocol is complete apart from a few caveats listed in issues/. Plans are in ROADMAP.md.

Contributing

docs/development.md covers building and testing, AGENTS.md the code style and the design principles. Found a bug that is not being fixed right now? Add a file to issues/.