fix: make overlay() ANSI-aware using visible character positions
This commit is contained in:
29
render.ts
29
render.ts
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user