fix: make overlay() ANSI-aware using visible character positions

This commit is contained in:
2026-05-03 21:25:34 -04:00
parent f8fac5fd45
commit 9951a11b29

View File

@@ -34,10 +34,33 @@ export function joinText(parts: string[], separator: string): string {
return parts.filter(Boolean).join(` ${separator} `);
}
// ANSI-Aware Slice - Extract a substring by visible character positions,
// preserving ANSI escape codes that precede visible characters.
function ansiSlice(text: string, start: number, end: number): string {
let output = "";
let visible = 0;
for (let i = 0; i < text.length; i++) {
if (text[i] === "\x1b") {
const match = text.slice(i).match(ANSI_PREFIX_RE);
if (match) {
if (visible >= start && visible < end) output += match[0];
i += match[0].length - 1;
continue;
}
}
if (visible >= start && visible < end) output += text[i];
visible++;
if (visible >= end) break;
}
return output;
}
export function overlay(base: string, text: string): string {
if (visibleWidth(text) >= visibleWidth(base)) return truncate(text, visibleWidth(base));
const start = Math.max(0, Math.floor((visibleWidth(base) - visibleWidth(text)) / 2));
return `${base.slice(0, start)}${text}${base.slice(start + visibleWidth(text))}`;
const baseWidth = visibleWidth(base);
const textWidth = visibleWidth(text);
if (textWidth >= baseWidth) return truncate(text, baseWidth);
const start = Math.max(0, Math.floor((baseWidth - textWidth) / 2));
return `${ansiSlice(base, 0, start)}${text}${ansiSlice(base, start + textWidth, baseWidth)}`;
}
function dimText(text: string, theme?: any): string {