Add emoji prefix.

This commit is contained in:
Greyson Parrelli
2026-08-13 09:56:34 -04:00
parent 0836cbc772
commit 6c7c3bfa63
7 changed files with 3328 additions and 31 deletions
+58 -5
View File
@@ -126,6 +126,8 @@ key.zig GDK keyval -> libghostty-vt key mapping
theme.zig colors libghostty-vt has no opinion about, per scheme
Settings.zig preferences: model, JSON on disk
SettingsDialog.zig the settings page
TabSettingsDialog.zig one tab's own settings, and the emoji picker
emoji.zig generated: every emoji the picker offers, and the search over them
```
A tab is a **view**, and a view holds one or more **panes** arranged in a
@@ -325,8 +327,12 @@ in principle, but a terminal grid is small.
- **Pane status in the tab strip**, driven by OSC 9;4, so a tab can say whether
it is working, waiting on you, or finished and still unanswered. See
[Agent status](#agent-status)
- **Renaming a tab**: `Ctrl+Shift+R`, right-click or double-click a tab row.
A typed name pins the label; clearing it hands the label back to the panes
- **Renaming a tab**: `Ctrl+Shift+R`, double-click a tab row, or **Rename** in
its right-click menu. A typed name pins the label; clearing it hands the
label back to the panes
- **An emoji in place of a tab's icon**, from **Settings** in the same
right-click menu, picked out of the full emoji set with keyword search. See
[Tab settings](#tab-settings)
- **Zooming a pane** to fill its tab and back, from the header button or
`Ctrl+Shift+Z`. Nothing closes and nothing moves — hidden panes keep running
and the split comes back exactly as it was. See [Zoom](#zoom)
@@ -383,6 +389,46 @@ that zoom is hiding. Closing the zoomed pane leaves zoom; a *different* pane
closing — a background shell exiting, say — does not, since what you are looking
at is still there.
## Tab settings
Right-clicking a tab row opens a small menu: **Rename**, which is the same
one-field popover `Ctrl+Shift+R` opens, and **Settings…**, which opens a window
belonging to that one tab. Double-clicking a row still goes straight to renaming
— it is the thing you do far more often than the other.
The window holds one setting today, **Emoji Label**: a glyph that stands in for
the pane icon in that tab's sidebar row. It replaces the icon rather than joining
it, since a row has one slot for "what is this tab" and the label needs the rest
of the width. **Use the pane icon** puts the icon back.
Picking one is a search box over a grid of the whole emoji set — 1906 glyphs, in
Unicode's own order and grouping, so it reads the way any other emoji keyboard
does. A glyph's tooltip is its name, for the ones you can't quite make out at
grid size.
Every whitespace-separated word in the query has to match, in any order, so
`green circle` and `circle green` both land on 🟢 while `circle` alone brings back
the whole family. Keywords are CLDR's, which is where `grin` finding 😀 comes
from, plus the group and subgroup each glyph belongs to — `fruit`, `arrow` and
`flags` each bring back a shelf — plus a table of synonyms for the words a
terminal user actually types: `deploy` finds 🚀, `hotfix` finds 🩹, `docker` finds
🐳, `rust` finds 🦀.
`src/emoji.zig` is generated by `tools/gen-emoji.py` from Unicode's
`emoji-test.txt` and CLDR's English annotations; run that rather than editing the
table, and add to its `SYNONYMS` when a glyph should answer to a word the data
files don't know. Two things it leaves out. Skin-tone variants, because just over
half of Unicode's 3944 sequences are the same gesture in five tones and a grid of
them is longer to look through rather than more complete — every base glyph is
there. And anything newer than emoji 16.0, because colour fonts trail Unicode by
a year or two and a glyph the font has never heard of draws as a hex-digit box,
which in a picker reads as a bug; `--max-version` raises the cutoff once fonts
have caught up.
The choice lives on the tab and not on disk, the same as a typed name: it lasts
as long as the tab does. Saving the tab as a layout does not carry it, since a
layout describes an arrangement of panes rather than what a row looks like.
## Agent status
Playpen is mostly used to keep several Claude Code sessions side by side, and
@@ -627,9 +673,16 @@ emit from and the client never sees it. Anything reachable only by clicking has
to be reached another way — a keyboard shortcut, or `zig build test` if the
thing being checked is logic rather than pixels.
`zig build test` runs the unit tests, which cover layout parsing and the
parameter/`$(...)` substitution pipeline. They are rooted at `Layouts.zig` so
the test binary never links GTK.
`zig build test` runs the unit tests, which cover layout parsing, the
parameter/`$(...)` substitution pipeline, and the emoji table and its search.
They build as two binaries, rooted at `Layouts.zig` and `emoji.zig`, so neither
links GTK — a test binary has one root, and those are the two files worth
testing in isolation that have nothing to do with each other.
`tools/gen-emoji.py` regenerates `src/emoji.zig` from Unicode's `emoji-test.txt`
and CLDR's annotations. It is not part of the build — its output is committed, so
a build never needs the network — and `--check` reports whether what is committed
is what it would write. See [Tab settings](#tab-settings).
Web panes need an EGL display and the headless compositor has no GPU, so
`shot.sh` forces the dev shell's mesa down to its software rasterizer. The mesa