Grapheme
Grapheme-aware string utilities. The PHP port of pi-tui's `visibleWidth()` / `truncateToWidth()` / `wrapTextWithAnsi()`, plus the CJK wide-glyph handling pi-tui does via `Intl.Segmenter` grapheme segmentation and a `cjkBreakRegex`. PHP has the `intl` extension, so we lean on `IntlBreakIterator` for grapheme boundaries and `IntlChar::isWide()` / `::isprint()` for the 1-vs-2 column width that CJK ideographs and wide emoji need. Everything here is pure — no state, no I/O. A TUI renderer or layout engine that cares about terminal columns (not byte count, not codepoint count) should go through these helpers rather than `strlen()` / `mb_strlen()`. Wide-character convention (matches xterm / Kitty / WezTerm): - CJK ideographs, full-width Latin, Hangul syllables, wide emoji → 2 cells - Combining marks, zero-width joiners, variation selectors → 0 cells - Everything else printable → 1 cell
Grapheme::visibleWidth()
public static function visibleWidth(string $text): intReturns the visible column width of `$text`: 2 per wide grapheme, 1 per normal grapheme, 0 per combining/zero-width grapheme. ANSI escape sequences (CSI, OSC, APC) are stripped first so styling does not inflate the count.
Parameters
| Name | Type | Description |
|---|---|---|
| $text | string |
Grapheme::truncateToWidth()
public static function truncateToWidth(string $text, int $width, string $ellipsis = '…'): stringTruncates `$text` to at most `$width` visible columns, optionally appending an ellipsis (default `…`, U+2026 — 1 cell wide) when the text was cut. Preserves ANSI escapes by truncating only the visible run and emitting a final reset so styles never bleed past the cut. Mirrors pi-tui's `truncateToWidth()` semantics.
Parameters
| Name | Type | Description |
|---|---|---|
| $text | string | |
| $width | int | |
| $ellipsis | string |
Grapheme::padEndToWidth()
public static function padEndToWidth(string $text, int $width): stringPads `$text` on the right with spaces until it is exactly `$width` visible columns wide. Truncates (no ellipsis) when already wider. Preserves ANSI escapes — the visible width is measured, the padding is appended as plain spaces after the final reset.
Parameters
| Name | Type | Description |
|---|---|---|
| $text | string | |
| $width | int |
Grapheme::graphemes()
public static function graphemes(string $plain): arraySplits `$plain` (no ANSI) into an array of graphemes, each a string of one or more UTF-8 codepoints that form a single user-perceived character. Uses `IntlBreakIterator::createCharacterInstance` when the `intl` extension is available; falls back to `preg_match_all('/./us')` (codepoint-level) when not.
Parameters
| Name | Type | Description |
|---|---|---|
| $plain | string |
Grapheme::graphemeWidth()
public static function graphemeWidth(string $grapheme): intReturns the visible column width of a single grapheme. Combining marks and zero-width joiners contribute 0; CJK wide chars and wide emoji contribute 2; everything else printable contributes 1.
Parameters
| Name | Type | Description |
|---|---|---|
| $grapheme | string |