fix(tui): add eslint-disable for intentional control character regex

The regex is intentionally matching ANSI escape sequences which contain
control characters. Added disable comment and fixed formatting.
This commit is contained in:
bee4come 2026-01-30 08:40:24 +00:00
parent 6933def1d7
commit 5c16ea9f55

View File

@ -143,11 +143,12 @@ export class SearchableSelectList implements Component {
for (const token of uniqueTokens) { for (const token of uniqueTokens) {
// CRITICAL FIX: Skip ANSI escape sequences to avoid breaking color codes // CRITICAL FIX: Skip ANSI escape sequences to avoid breaking color codes
// Split text into ANSI and visible parts, only highlight visible parts // Split text into ANSI and visible parts, only highlight visible parts
// eslint-disable-next-line no-control-regex -- intentional: matching ANSI escape sequences
const ansiRegex = /\x1b\[[0-9;]*m/g; const ansiRegex = /\x1b\[[0-9;]*m/g;
const parts: Array<{ text: string; isAnsi: boolean }> = []; const parts: Array<{ text: string; isAnsi: boolean }> = [];
let lastIndex = 0; let lastIndex = 0;
let match: RegExpExecArray | null; let match: RegExpExecArray | null;
while ((match = ansiRegex.exec(text)) !== null) { while ((match = ansiRegex.exec(text)) !== null) {
if (match.index > lastIndex) { if (match.index > lastIndex) {
parts.push({ text: text.slice(lastIndex, match.index), isAnsi: false }); parts.push({ text: text.slice(lastIndex, match.index), isAnsi: false });
@ -158,7 +159,7 @@ export class SearchableSelectList implements Component {
if (lastIndex < text.length) { if (lastIndex < text.length) {
parts.push({ text: text.slice(lastIndex), isAnsi: false }); parts.push({ text: text.slice(lastIndex), isAnsi: false });
} }
// Only highlight in non-ANSI parts // Only highlight in non-ANSI parts
const regex = this.getCachedRegex(token); const regex = this.getCachedRegex(token);
result = parts result = parts
@ -168,7 +169,7 @@ export class SearchableSelectList implements Component {
return part.text.replace(regex, (m) => this.theme.matchHighlight(m)); return part.text.replace(regex, (m) => this.theme.matchHighlight(m));
}) })
.join(""); .join("");
// Update text for next token iteration // Update text for next token iteration
text = result; text = result;
} }
@ -233,7 +234,7 @@ export class SearchableSelectList implements Component {
private ensureLineWidth(text: string, width: number): string { private ensureLineWidth(text: string, width: number): string {
// Use pi-tui's visibleWidth for accurate measurement // Use pi-tui's visibleWidth for accurate measurement
const currentWidth = visibleWidth(text); const currentWidth = visibleWidth(text);
if (currentWidth <= width) { if (currentWidth <= width) {
return text; return text;
} }
@ -273,9 +274,7 @@ export class SearchableSelectList implements Component {
const truncatedDesc = truncateToWidth(item.description, remainingWidth, ""); const truncatedDesc = truncateToWidth(item.description, remainingWidth, "");
// Highlight first, then apply theme - avoids breaking ANSI codes // Highlight first, then apply theme - avoids breaking ANSI codes
const highlightedDesc = this.highlightMatch(truncatedDesc, query); const highlightedDesc = this.highlightMatch(truncatedDesc, query);
const descText = isSelected const descText = isSelected ? highlightedDesc : this.theme.description(highlightedDesc);
? highlightedDesc
: this.theme.description(highlightedDesc);
const line = `${prefix}${valueText}${spacing}${descText}`; const line = `${prefix}${valueText}${spacing}${descText}`;
const rendered = isSelected ? this.theme.selectedText(line) : line; const rendered = isSelected ? this.theme.selectedText(line) : line;
return this.ensureLineWidth(rendered, width); return this.ensureLineWidth(rendered, width);