fix(coding-agent): make footer truncation width-aware

Use visibleWidth/truncateToWidth for footer path and stats truncation so wide Unicode text cannot overflow terminal width.

Add regression tests for wide session/model/provider names and document the fix in changelog.

closes #1833
This commit is contained in:
Mario Zechner 2026-03-05 20:44:16 +01:00
parent dabcda0db3
commit 3de8c48692
3 changed files with 123 additions and 23 deletions

View file

@ -104,18 +104,6 @@ export class FooterComponent implements Component {
pwd = `${pwd}${sessionName}`;
}
// Truncate path if too long to fit width
if (pwd.length > width) {
const half = Math.floor(width / 2) - 2;
if (half > 1) {
const start = pwd.slice(0, half);
const end = pwd.slice(-(half - 1));
pwd = `${start}...${end}`;
} else {
pwd = pwd.slice(0, Math.max(1, width));
}
}
// Build stats line
const statsParts = [];
if (totalInput) statsParts.push(`${formatTokens(totalInput)}`);
@ -155,9 +143,7 @@ export class FooterComponent implements Component {
// If statsLeft is too wide, truncate it
if (statsLeftWidth > width) {
// Truncate statsLeft to fit width (no room for right side)
const plainStatsLeft = statsLeft.replace(/\x1b\[[0-9;]*m/g, "");
statsLeft = `${plainStatsLeft.substring(0, width - 3)}...`;
statsLeft = truncateToWidth(statsLeft, width, "...");
statsLeftWidth = visibleWidth(statsLeft);
}
@ -193,13 +179,11 @@ export class FooterComponent implements Component {
} else {
// Need to truncate right side
const availableForRight = width - statsLeftWidth - minPadding;
if (availableForRight > 3) {
// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)
const plainRightSide = rightSide.replace(/\x1b\[[0-9;]*m/g, "");
const truncatedPlain = plainRightSide.substring(0, availableForRight);
// For simplicity, just use plain truncated version (loses color, but fits)
const padding = " ".repeat(width - statsLeftWidth - truncatedPlain.length);
statsLine = statsLeft + padding + truncatedPlain;
if (availableForRight > 0) {
const truncatedRight = truncateToWidth(rightSide, availableForRight, "");
const truncatedRightWidth = visibleWidth(truncatedRight);
const padding = " ".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));
statsLine = statsLeft + padding + truncatedRight;
} else {
// Not enough space for right side at all
statsLine = statsLeft;
@ -213,7 +197,8 @@ export class FooterComponent implements Component {
const remainder = statsLine.slice(statsLeft.length); // padding + rightSide
const dimRemainder = theme.fg("dim", remainder);
const lines = [theme.fg("dim", pwd), dimStatsLeft + dimRemainder];
const pwdLine = truncateToWidth(theme.fg("dim", pwd), width, theme.fg("dim", "..."));
const lines = [pwdLine, dimStatsLeft + dimRemainder];
// Add extension statuses on a single line, sorted by key alphabetically
const extensionStatuses = this.footerData.getExtensionStatuses();