Skip to content

v0.5.0

Choose a tag to compare

@lloydrichards lloydrichards released this 21 Sep 20:41
· 171 commits to main since this release

v0.5.0 (2025-09-21)

While investigating an emoji rendering bug, I discovered the complexity of accurate column width calculations in TUIs. To address this, I've added a new Width module that calculates the visible width of strings containing emojis, ANSI escape sequences, and East Asian characters.

The implementation is inspired by string-width, the key change is that all emojis are now treated as full-width (2 columns), which ensures proper alignment but may introduce extra spacing when displaying emojis in existing layouts:

import * as Width from "./src/Width";

// Basic ASCII strings
Width.ofString("hello");           // => 5
Width.ofString("Hello, world!");   // => 13

// ANSI escape codes are stripped automatically
Width.ofString("\x1b[31mred\x1b[0m");    // => 3 (color codes ignored)
Width.ofString("\x1b[1;32mbold\x1b[0m"); // => 4 (style codes ignored)

// East Asian characters are 2 columns wide
Width.ofString("你好");       // => 4 (2 Chinese characters)
Width.ofString("こんにちは");  // => 10 (5 Japanese characters)
Width.ofString("hello你好");  // => 9 (5 ASCII + 4 wide chars)

// All emojis are treated as 2 columns wide
Width.ofString("👋");         // => 2 (simple emoji)
Width.ofString("👩‍💻");         // => 2 (complex multi-char emoji)
Width.ofString("👍🏻");         // => 2 (emoji with skin tone)
Width.ofString("hello 👋");   // => 8 (text + emoji)

// Zero-width and combining characters
Width.ofString("café");       // => 4 (precomposed)
Width.ofString("cafe\u0301"); // => 4 (decomposed é with combining mark)

The module handles edge cases including multi-character emoji sequences, Unicode combining marks, and mixed content, ensuring accurate width calculations for proper terminal layout rendering.

Features

  • ✨ add Width module

Fixes

  • 🐛 render emojis seperately (#2)

Full Changelog: v0.4.0...v0.5.0