Summary
terminal-pilot treats a joined emoji as multiple terminal cells rather than one
two-column grapheme. Absolute-position incremental redraw then leaves stale
emoji components and shifts following text. This affects read-screen as well
as screenshots, so it is not only a missing-font rendering issue.
Reproduced September 5, 2026 on Linux, Node 22.22.0, terminal-pilot 0.0.1.
The reproduction uses only Node stdout/stdin and ANSI, without Toolcraft or
application UI components.
Reproduce
Save this as /tmp/emoji-redraw.mjs:
process.stdout.write("\x1b[2J\x1b[H|A👩💻B|\r\n|A🚀B|\r\n");
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on("data", (chunk) => {
if (chunk.includes(0x12)) process.stdout.write("\x1b[1;3H \x1b[2;3H ");
if (chunk.includes(0x03)) {
process.stdin.setRawMode(false);
process.stdin.pause();
}
});
terminal-pilot create-session --session emoji-redraw --cwd /tmp --cols 64 --rows 12 node /tmp/emoji-redraw.mjs
terminal-pilot press-key Control+r --session emoji-redraw
terminal-pilot read-screen --session emoji-redraw --output json
terminal-pilot screenshot --session emoji-redraw -o /tmp/emoji-redraw.png
terminal-pilot close-session --session emoji-redraw
Both emoji should occupy two columns. Writing two spaces at column 3 should
therefore leave both rows as |A B|.
Actual read-screen rows:
The first row retains U+200D and U+1F4BB, with B| shifted right. The single
code-point rocket control behaves correctly. The PNG also retains the laptop
component (shown as a missing-font box with the installed font).
Impact and suggested coverage
Grapheme-aware TUI editors erase the correct two cells, but terminal-pilot shows
leftover text and displaced borders. This produces false layout and deletion
failures during terminal QA. No application output workaround should be needed.
Please cover joined emoji, variation selectors, flags, and combining sequences
in the emulator, including graphemes split across writes and absolute-position
overwrites of wide cells. Assert identical final cells for full-frame output
and incremental redraw, then verify screenshot geometry separately from font
fallback support. Local QA evidence includes an executable
reproduction plus visually inspected before/after PNGs.
Summary
terminal-pilot treats a joined emoji as multiple terminal cells rather than one
two-column grapheme. Absolute-position incremental redraw then leaves stale
emoji components and shifts following text. This affects
read-screenas wellas screenshots, so it is not only a missing-font rendering issue.
Reproduced September 5, 2026 on Linux, Node 22.22.0, terminal-pilot 0.0.1.
The reproduction uses only Node stdout/stdin and ANSI, without Toolcraft or
application UI components.
Reproduce
Save this as
/tmp/emoji-redraw.mjs:Both emoji should occupy two columns. Writing two spaces at column 3 should
therefore leave both rows as
|A B|.Actual
read-screenrows:The first row retains U+200D and U+1F4BB, with
B|shifted right. The singlecode-point rocket control behaves correctly. The PNG also retains the laptop
component (shown as a missing-font box with the installed font).
Impact and suggested coverage
Grapheme-aware TUI editors erase the correct two cells, but terminal-pilot shows
leftover text and displaced borders. This produces false layout and deletion
failures during terminal QA. No application output workaround should be needed.
Please cover joined emoji, variation selectors, flags, and combining sequences
in the emulator, including graphemes split across writes and absolute-position
overwrites of wide cells. Assert identical final cells for full-frame output
and incremental redraw, then verify screenshot geometry separately from font
fallback support. Local QA evidence includes an executable
reproduction plus visually inspected before/after PNGs.