@teaui/core
Version:
A high-level terminal UI library for Node
593 lines • 20.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.charWidth = charWidth;
exports.stringSize = stringSize;
exports.lineWidth = lineWidth;
exports.getLocale = getLocale;
exports.setLocale = setLocale;
exports.words = words;
exports.printableChars = printableChars;
exports.ansiLocations = ansiLocations;
exports.removeAnsi = removeAnsi;
const BG_DRAW = '\x14';
/**
* Returns the number of *cells* that the first character of the string takes up.
*
* "Cell" refers to a terminal space: ASCII characters take 1 cell, Emoji and Asian
* characters take 2 cells. ANSI codes (\x1b[...) return 0.
*
* This code came straight from blessed, and includes snippets from
* https://github.com/vangie/east-asian-width and
* https://github.com/komagata/eastasianwidth (last updated ~2015)
*
* Note: does not check to make sure 'str' is only one character
*/
function charWidth(str) {
// this special character is used by viewport.paint. If you are copying this code
// for your own purposes, you should probably remove this.
if (str === BG_DRAW) {
return 1;
}
// added: ANSI formatter support
// eslint-disable-next-line no-control-regex
if (!str.length || ansiRegex().test(str)) {
return 0;
}
// added: Emoji support
if (str.length > 1 && /^\p{Extended_Pictographic}/u.test(str)) {
// added: whitelist? ug.
if (str === '▫️' ||
str === '◻️' ||
str === '◼︎' ||
str === '▪️' ||
str === '◼️') {
return 1;
}
return 2;
}
var point = typeof str !== 'number' ? str.codePointAt(0) : str;
// nul
if (point === 0)
return 0;
// tab
if (point === 0x09) {
return 1;
}
// 8-bit control characters (2-width according to unicode??)
if (point < 32 || (point >= 0x7f && point < 0xa0)) {
return 0;
}
// search table of non-spacing characters
// is ucs combining or C0/C1 control character
if (isCombiningCode(point)) {
return 0;
}
// check for double-wide
// if (point >= 0x1100
// && (point <= 0x115f // Hangul Jamo init. consonants
// || point === 0x2329 || point === 0x232a
// || (point >= 0x2e80 && point <= 0xa4cf
// && point !== 0x303f) // CJK ... Yi
// || (point >= 0xac00 && point <= 0xd7a3) // Hangul Syllables
// || (point >= 0xf900 && point <= 0xfaff) // CJK Compatibility Ideographs
// || (point >= 0xfe10 && point <= 0xfe19) // Vertical forms
// || (point >= 0xfe30 && point <= 0xfe6f) // CJK Compatibility Forms
// || (point >= 0xff00 && point <= 0xff60) // Fullwidth Forms
// || (point >= 0xffe0 && point <= 0xffe6)
// || (point >= 0x20000 && point <= 0x2fffd)
// || (point >= 0x30000 && point <= 0x3fffd))) {
// return 2;
// }
// check for double-wide
if (0x3000 === point ||
(0xff01 <= point && point <= 0xff60) ||
(0xffe0 <= point && point <= 0xffe6)) {
return 2;
}
if ((0x1100 <= point && point <= 0x115f) ||
(0x11a3 <= point && point <= 0x11a7) ||
(0x11fa <= point && point <= 0x11ff) ||
(0x2329 <= point && point <= 0x232a) ||
(0x2e80 <= point && point <= 0x2e99) ||
(0x2e9b <= point && point <= 0x2ef3) ||
(0x2f00 <= point && point <= 0x2fd5) ||
(0x2ff0 <= point && point <= 0x2ffb) ||
(0x3001 <= point && point <= 0x303e) ||
(0x3041 <= point && point <= 0x3096) ||
(0x3099 <= point && point <= 0x30ff) ||
(0x3105 <= point && point <= 0x312d) ||
(0x3131 <= point && point <= 0x318e) ||
(0x3190 <= point && point <= 0x31ba) ||
(0x31c0 <= point && point <= 0x31e3) ||
(0x31f0 <= point && point <= 0x321e) ||
(0x3220 <= point && point <= 0x3247) ||
(0x3250 <= point && point <= 0x32fe) ||
(0x3300 <= point && point <= 0x4dbf) ||
(0x4e00 <= point && point <= 0xa48c) ||
(0xa490 <= point && point <= 0xa4c6) ||
(0xa960 <= point && point <= 0xa97c) ||
(0xac00 <= point && point <= 0xd7a3) ||
(0xd7b0 <= point && point <= 0xd7c6) ||
(0xd7cb <= point && point <= 0xd7fb) ||
(0xf900 <= point && point <= 0xfaff) ||
(0xfe10 <= point && point <= 0xfe19) ||
(0xfe30 <= point && point <= 0xfe52) ||
(0xfe54 <= point && point <= 0xfe66) ||
(0xfe68 <= point && point <= 0xfe6b) ||
(0x1b000 <= point && point <= 0x1b001) ||
(0x1f200 <= point && point <= 0x1f202) ||
(0x1f210 <= point && point <= 0x1f23a) ||
(0x1f240 <= point && point <= 0x1f248) ||
(0x1f250 <= point && point <= 0x1f251) ||
(0x20000 <= point && point <= 0x2f73f) ||
(0x2b740 <= point && point <= 0x2fffd) ||
(0x30000 <= point && point <= 0x3fffd)) {
return 2;
}
// CJK Ambiguous
// http://www.unicode.org/reports/tr11/
// http://www.unicode.org/reports/tr11/#Ambiguous
if (process.env.NCURSES_CJK_WIDTH) {
if (0x00a1 === point ||
0x00a4 === point ||
(0x00a7 <= point && point <= 0x00a8) ||
0x00aa === point ||
(0x00ad <= point && point <= 0x00ae) ||
(0x00b0 <= point && point <= 0x00b4) ||
(0x00b6 <= point && point <= 0x00ba) ||
(0x00bc <= point && point <= 0x00bf) ||
0x00c6 === point ||
0x00d0 === point ||
(0x00d7 <= point && point <= 0x00d8) ||
(0x00de <= point && point <= 0x00e1) ||
0x00e6 === point ||
(0x00e8 <= point && point <= 0x00ea) ||
(0x00ec <= point && point <= 0x00ed) ||
0x00f0 === point ||
(0x00f2 <= point && point <= 0x00f3) ||
(0x00f7 <= point && point <= 0x00fa) ||
0x00fc === point ||
0x00fe === point ||
0x0101 === point ||
0x0111 === point ||
0x0113 === point ||
0x011b === point ||
(0x0126 <= point && point <= 0x0127) ||
0x012b === point ||
(0x0131 <= point && point <= 0x0133) ||
0x0138 === point ||
(0x013f <= point && point <= 0x0142) ||
0x0144 === point ||
(0x0148 <= point && point <= 0x014b) ||
0x014d === point ||
(0x0152 <= point && point <= 0x0153) ||
(0x0166 <= point && point <= 0x0167) ||
0x016b === point ||
0x01ce === point ||
0x01d0 === point ||
0x01d2 === point ||
0x01d4 === point ||
0x01d6 === point ||
0x01d8 === point ||
0x01da === point ||
0x01dc === point ||
0x0251 === point ||
0x0261 === point ||
0x02c4 === point ||
0x02c7 === point ||
(0x02c9 <= point && point <= 0x02cb) ||
0x02cd === point ||
0x02d0 === point ||
(0x02d8 <= point && point <= 0x02db) ||
0x02dd === point ||
0x02df === point ||
(0x0300 <= point && point <= 0x036f) ||
(0x0391 <= point && point <= 0x03a1) ||
(0x03a3 <= point && point <= 0x03a9) ||
(0x03b1 <= point && point <= 0x03c1) ||
(0x03c3 <= point && point <= 0x03c9) ||
0x0401 === point ||
(0x0410 <= point && point <= 0x044f) ||
0x0451 === point ||
0x2010 === point ||
(0x2013 <= point && point <= 0x2016) ||
(0x2018 <= point && point <= 0x2019) ||
(0x201c <= point && point <= 0x201d) ||
(0x2020 <= point && point <= 0x2022) ||
(0x2024 <= point && point <= 0x2027) ||
0x2030 === point ||
(0x2032 <= point && point <= 0x2033) ||
0x2035 === point ||
0x203b === point ||
0x203e === point ||
0x2074 === point ||
0x207f === point ||
(0x2081 <= point && point <= 0x2084) ||
0x20ac === point ||
0x2103 === point ||
0x2105 === point ||
0x2109 === point ||
0x2113 === point ||
0x2116 === point ||
(0x2121 <= point && point <= 0x2122) ||
0x2126 === point ||
0x212b === point ||
(0x2153 <= point && point <= 0x2154) ||
(0x215b <= point && point <= 0x215e) ||
(0x2160 <= point && point <= 0x216b) ||
(0x2170 <= point && point <= 0x2179) ||
0x2189 === point ||
(0x2190 <= point && point <= 0x2199) ||
(0x21b8 <= point && point <= 0x21b9) ||
0x21d2 === point ||
0x21d4 === point ||
0x21e7 === point ||
0x2200 === point ||
(0x2202 <= point && point <= 0x2203) ||
(0x2207 <= point && point <= 0x2208) ||
0x220b === point ||
0x220f === point ||
0x2211 === point ||
0x2215 === point ||
0x221a === point ||
(0x221d <= point && point <= 0x2220) ||
0x2223 === point ||
0x2225 === point ||
(0x2227 <= point && point <= 0x222c) ||
0x222e === point ||
(0x2234 <= point && point <= 0x2237) ||
(0x223c <= point && point <= 0x223d) ||
0x2248 === point ||
0x224c === point ||
0x2252 === point ||
(0x2260 <= point && point <= 0x2261) ||
(0x2264 <= point && point <= 0x2267) ||
(0x226a <= point && point <= 0x226b) ||
(0x226e <= point && point <= 0x226f) ||
(0x2282 <= point && point <= 0x2283) ||
(0x2286 <= point && point <= 0x2287) ||
0x2295 === point ||
0x2299 === point ||
0x22a5 === point ||
0x22bf === point ||
0x2312 === point ||
(0x2460 <= point && point <= 0x24e9) ||
(0x24eb <= point && point <= 0x254b) ||
(0x2550 <= point && point <= 0x2573) ||
(0x2580 <= point && point <= 0x258f) ||
(0x2592 <= point && point <= 0x2595) ||
(0x25a0 <= point && point <= 0x25a1) ||
(0x25a3 <= point && point <= 0x25a9) ||
(0x25b2 <= point && point <= 0x25b3) ||
(0x25b6 <= point && point <= 0x25b7) ||
(0x25bc <= point && point <= 0x25bd) ||
(0x25c0 <= point && point <= 0x25c1) ||
(0x25c6 <= point && point <= 0x25c8) ||
0x25cb === point ||
(0x25ce <= point && point <= 0x25d1) ||
(0x25e2 <= point && point <= 0x25e5) ||
0x25ef === point ||
(0x2605 <= point && point <= 0x2606) ||
0x2609 === point ||
(0x260e <= point && point <= 0x260f) ||
(0x2614 <= point && point <= 0x2615) ||
0x261c === point ||
0x261e === point ||
0x2640 === point ||
0x2642 === point ||
(0x2660 <= point && point <= 0x2661) ||
(0x2663 <= point && point <= 0x2665) ||
(0x2667 <= point && point <= 0x266a) ||
(0x266c <= point && point <= 0x266d) ||
0x266f === point ||
(0x269e <= point && point <= 0x269f) ||
(0x26be <= point && point <= 0x26bf) ||
(0x26c4 <= point && point <= 0x26cd) ||
(0x26cf <= point && point <= 0x26e1) ||
0x26e3 === point ||
(0x26e8 <= point && point <= 0x26ff) ||
0x273d === point ||
0x2757 === point ||
(0x2776 <= point && point <= 0x277f) ||
(0x2b55 <= point && point <= 0x2b59) ||
(0x3248 <= point && point <= 0x324f) ||
(0xe000 <= point && point <= 0xf8ff) ||
(0xfe00 <= point && point <= 0xfe0f) ||
0xfffd === point ||
(0x1f100 <= point && point <= 0x1f10a) ||
(0x1f110 <= point && point <= 0x1f12d) ||
(0x1f130 <= point && point <= 0x1f169) ||
(0x1f170 <= point && point <= 0x1f19a) ||
(0xe0100 <= point && point <= 0xe01ef) ||
(0xf0000 <= point && point <= 0xffffd) ||
(0x100000 <= point && point <= 0x10fffd)) {
return +process.env.NCURSES_CJK_WIDTH || 1;
}
}
return 1;
}
function stringSize(str, maxWidth) {
if (Array.isArray(str)) {
if (maxWidth != null) {
return str.reduce((size, line) => {
const width = lineWidth(line);
const height = Math.max(1, Math.ceil(width / maxWidth));
size.width = Math.max(size.width, width);
size.height += height;
return size;
}, { width: 0, height: 0 });
}
return { width: Math.max(...str.map(lineWidth)), height: str.length };
}
else {
return stringSize(str.split('\n'));
}
}
function lineWidth(str) {
var width = 0;
for (const char of Array.isArray(str) ? str : printableChars(str)) {
if (str === '\n') {
break;
}
width += charWidth(char);
}
return width;
}
let locale = process.env.LANG?.split('.')[0]?.slice(0, 2) ?? 'en';
let graphemesSegmenter = new Intl.Segmenter(locale);
let wordsSegmenter = new Intl.Segmenter(locale, { granularity: 'word' });
function getLocale() {
return locale;
}
function setLocale(value) {
locale = value;
graphemesSegmenter = new Intl.Segmenter(locale);
wordsSegmenter = new Intl.Segmenter(locale, { granularity: 'word' });
}
function words(input) {
if (Array.isArray(input)) {
input = input.join('');
}
const ansiData = ansiLocations(input, false);
input = removeAnsi(input);
let strIndex = 0;
const segments = Array.from(wordsSegmenter.segment(input)).map(({ segment }) => segment);
const parts = segments.map(segment => {
let offset = segment.length;
while (ansiData.length &&
strIndex + segment.length >= ansiData.at(0).start) {
const { start, ansi } = ansiData.shift();
const lhs = segment.slice(0, start - strIndex);
const rhs = segment.slice(start - strIndex);
segment = lhs + ansi + rhs;
offset += ansi.length;
}
strIndex += offset;
return segment;
});
let offset = 0;
let words = [];
for (let segment of parts) {
const currentOffset = offset;
let chars = printableChars(segment);
offset += chars.length;
words.push([chars, currentOffset]);
}
return words;
}
function printableChars(str) {
if (str.length === 0) {
return [];
}
if (str.length === 1) {
return [str];
}
const chars = [];
let locations = ansiLocations(str, true);
let prevIndex = 0;
for (const { start, stop, ansi } of locations) {
// if the string starts with an ansi sequence, we don't prepend any graphemes,
// and if there are NO ansi sequences we will pick up on the entire string
// thanks to `includeLast` option.
if (prevIndex < start) {
const input = str.slice(prevIndex, start);
for (const { segment: char } of graphemesSegmenter.segment(input)) {
chars.push(char);
}
}
if (ansi) {
chars.push(ansi);
}
prevIndex = stop;
}
return chars;
}
/**
* Copied this (on 2025-11-23) from
* https://github.com/chalk/ansi-regex/blob/main/index.js
*/
function ansiRegex() {
// Valid string terminator sequences are BEL, ESC\, and 0x9c
const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)';
const pattern = [
`[\\u001B\\u009B][[\\]()
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
].join('|');
return new RegExp(pattern, 'g');
}
function ansiLocations(input, includeLast) {
// eslint-disable-next-line no-control-regex
const locations = [...input.matchAll(ansiRegex())].map(({ 0: match, index }) => ({
start: index,
stop: index + match.length,
ansi: match,
}));
if (includeLast &&
(!locations.length || locations.at(-1).stop < input.length)) {
const last = {
start: input.length,
stop: input.length,
ansi: '',
};
locations.push(last);
}
return locations;
}
function removeAnsi(input) {
// eslint-disable-next-line no-control-regex
return input.replaceAll(ansiRegex(), '');
}
const combiningTable = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
];
const _isCombiningCode = combiningTable.reduce(function (map, row) {
for (var i = row[0]; i <= row[1]; i++) {
map.set(i, true);
}
return map;
}, new Map());
function isCombiningCode(code) {
return _isCombiningCode.get(code) ?? false;
}
//# sourceMappingURL=unicode.js.map