Ubunatic Coding
emojig logo emojig

emojig

A terminal emoji picker that respects your terminal. One static Zig binary, inline TUI or floating GUI, zero heap allocations in the search loop, zero daemons, zero Electron.

Honest by construction: the picker logs its own RSS to /tmp/emojig.log on every exit — you can check the numbers below on your machine.

$ curl -fsSL https://ubunatic.com/emojig/install.sh | sh
⭣ v0.2.0

the numbers

Measured, not marketed.

< 900 KB
static binary (ReleaseSmall, musl)
< 5 MB
RSS, self-reported at exit
2,249
emojis, compiled into the binary
0
daemons, sockets, runtime deps

The emoji database is embedded with @embedFile; queries return string slices pointing straight into the binary's read-only memory. No JSON parsing, no disk reads, no allocations at query time.

────────────────────────────────────────────────────────────────────────────

playground

This is the real search engine and grid logic, ported 1:1 from the Zig source to JS — same scoring, same fallbacks, same keybindings. Type a command at the prompt, then hit Ctrl+E.

Playground HUD

At the prompt, type echo  then Ctrl+E opens the picker. Fuzzy-search, arrows navigate, Enter inserts into the command line. In the picker, try ? and ?? for the built-in help pages, and the e:/t: width filters. Tab cycles themes, Esc bails out.

Click terminal to focus keys
zsh — emojig widget Ctrl+E Shell

why another emoji picker

Because the existing ones are either a browser in disguise, a desktop-environment plugin, or a script that leaves your terminal in raw mode when it crashes.

🚀

Zero-allocation core

The interactive loop performs no heap allocations. Search scores 2,249 entries per keystroke against an @embedFile'd database and returns slices into read-only binary memory. Written in Zig, compiled with ReleaseSmall.

🖥️

One binary, two faces

fzf-style auto-detection: a TTY means inline TUI at your prompt; a desktop hotkey (no TTY) means a compact floating window via your own terminal (foot, kitty, alacritty, …). No daemon, no IPC, no background anything.

🔁

Single-instance GUI toggle

The same hotkey opens and closes the floating picker. Implemented with a pidfile and a /proc/<pid>/cmdline check — against PID reuse — in ~70 lines of plain POSIX. Press it twice: no window stacking, ever.

🛡️

Terminal-state paranoia

A custom Zig panic handler plus SIGINT/SIGTERM handlers restore termios, disable mouse tracking, and reset the cursor on every exit path — including crashes. Your prompt comes back clean.

🧠

Search that forgives

Subsequence scoring with word-start and consecutive-run bonuses, AND-matching for multi-word queries, and automatic fallbacks for plurals (cars→car), verb stems (running→run), and trailing e. Plus e:/t: width filters and paged in-picker help (?, ??).

📋

Spec-driven UI, two implementations

Layout, themes, keybindings, and all UI strings (10 languages) live in JSON specs, embedded at compile time. The Go port (mojigo) consumes the same specs — one source of truth, verified by tests in both languages.

────────────────────────────────────────────────────────────────────────────

demo reels

Demos here are not screen-recorded by hand. Each one is a .reel script checked into spec/reels/ and recorded reproducibly with wayreel in a headless compositor. If a demo looks wrong, the script is the bug report.

spec/reels/tui-dark.json → emojig --tui recorded ✓
spec/reels/gui.json → emojig --gui recorded ✓

Scripted next — titles map 1:1 to .reel files; steps are sketched, recordings follow:

shell-widget.reel

Ctrl+E Shell Widget

Half-typed commit message, Ctrl+E, fuzzy "rock", Enter — 🚀 lands in the command line. Prompt stays pristine.

scripted · not yet recorded
search-filters.reel

Search Tricks

Stems, plurals, AND-terms, and the e:/t: width filters — the engine being smarter than a substring.

scripted · not yet recorded
help-pages.reel

Paged Help: ? and ??

The picker documents itself in 25 columns; backspace walks back from page 2 to the grid.

scripted · not yet recorded
gui-toggle.reel

Single-Instance GUI Toggle

One hotkey: open, close, open — never two windows. Pidfile + /proc check, no daemon.

scripted · not yet recorded
themes.reel

Themes & OSC 11

The picker asks the terminal for its background color and matches it; Tab cycles 🌙/🌞/🔆 live.

scripted · not yet recorded
mru-recents.reel

Recents First

Pick 🔥 once and it's in cell #1 next time. MRU survives restarts via one POSIX file write.

scripted · not yet recorded

what's next

A roadmap, not a promise of dates. Tracked in the issues directory — in the repo, like everything else.

high priority

Many more emojis high

2,249 is the curated core; the full Unicode set is much bigger. Grow the database without giving up the compile-time packing or the <900 KB budget.

Search specific Unicode symbols high

⌘ ∑ ≠ × — math, arrows, technical and typographic symbols, searchable by their real Unicode names.

Color ↔ plain toggle high

Switch a selected color emoji to its plain text presentation (❤️ → ❤) before copying — for commit messages, logs, and ASCII-leaning contexts.

Better search, MRU & ranking high

Smarter synonym ranking, frecency over plain MRU, fewer surprising top hits. Still zero-allocation, still a plain file, still no daemon.

medium priority

Skin-tone modifiers med

Pick 👋, choose 👋🏽 — a modifier sub-picker without bloating the grid or the binary.

Powerline & Nerd Font glyphs med

Octocat, git branch arrows, and friends — with detection of whether your font can actually render them before offering them.

low priority

Cell-perfect box-drawing borders low

Pretty borders around the grid would be nice, but the picker has to render correctly across too many terminal stacks to bet the UI on it.

Proper packages very low

AUR, Nix, and Homebrew can wait — the curl | sh and make install paths work today.

────────────────────────────────────────────────────────────────────────────

keys

Ctrl+E open the picker at the shell prompt; pick lands in the command line
↑↓←→ 2D grid navigation, wraps at the edges
Enter / click copy selection (and insert, in widget mode)
Tab cycle theme dark / light / system, persisted
? ?? help pages 1 and 2
Esc / Ctrl+C exit; terminal state fully restored

setup

  • OS: Linux on x86_64 or aarch64; works over SSH and on raw VTs.
  • GUI terminals: foot (cell-precise), or kitty, alacritty, wezterm, ghostty, konsole, gnome-terminal, ptyxis.
  • Clipboard: wl-copy (Wayland) or xclip (X11), if present.
  • Shell widget: source ~/.local/share/emojig/shell/emojig.<shell> in your bash/zsh/fish config enables Ctrl+E.