bktide
Version:
Command-line interface for Buildkite CI/CD workflows with rich shell completions (Fish, Bash, Zsh) and Alfred workflow integration for macOS power users
111 lines • 3.71 kB
JavaScript
/**
* Terminal width utilities for responsive display
*/
/**
* Strip ANSI escape codes from a string
* Used for accurate length calculations
*/
export function stripAnsi(str) {
// Comprehensive regex for ANSI escape codes
const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
return str.replace(ansiRegex, '');
}
/**
* Get the current terminal width
* Falls back to 80 columns if not available
*/
export function termWidth() {
// Check environment variable first (for testing)
const envColumns = process.env.COLUMNS ? parseInt(process.env.COLUMNS, 10) : undefined;
const columns = envColumns || process.stdout.columns || 80;
// Ensure minimum width of 40 for readability
return Math.max(40, columns);
}
/**
* Truncate text to fit within a maximum width
* Adds ellipsis if text is truncated
*/
export function truncate(text, maxWidth) {
if (text.length <= maxWidth)
return text;
if (maxWidth <= 1)
return text[0] || '';
return text.slice(0, Math.max(0, maxWidth - 1)) + '…';
}
/**
* Wrap text to fit within a maximum width
* Returns an array of lines
*/
export function wrapText(text, maxWidth) {
if (maxWidth <= 0)
return [text];
if (text.length <= maxWidth)
return [text];
const words = text.split(/\s+/);
const lines = [];
let currentLine = '';
for (const word of words) {
// If a single word is longer than maxWidth, we need to break it
if (word.length > maxWidth) {
// Finish current line if it has content
if (currentLine) {
lines.push(currentLine.trim());
currentLine = '';
}
// Break the long word into chunks
let remaining = word;
while (remaining.length > maxWidth) {
lines.push(remaining.slice(0, maxWidth - 1) + '-');
remaining = remaining.slice(maxWidth - 1);
}
currentLine = remaining;
}
else if ((currentLine + ' ' + word).trim().length > maxWidth) {
// Start a new line
if (currentLine)
lines.push(currentLine.trim());
currentLine = word;
}
else {
// Add word to current line
currentLine += (currentLine ? ' ' : '') + word;
}
}
// Add any remaining text
if (currentLine)
lines.push(currentLine.trim());
return lines.length > 0 ? lines : [''];
}
/**
* Calculate column widths for a table based on terminal width
* Distributes available width evenly among columns
*/
export function calculateColumnWidths(numColumns, terminalWidth, padding = 2) {
if (numColumns <= 0)
return [];
// Account for padding between columns (not after last column)
const totalPadding = padding * (numColumns - 1);
const availableWidth = Math.max(numColumns, terminalWidth - totalPadding);
// Distribute width evenly
const baseWidth = Math.floor(availableWidth / numColumns);
const remainder = availableWidth % numColumns;
// Create array with base widths
const widths = new Array(numColumns).fill(baseWidth);
// Distribute remainder to first columns
for (let i = 0; i < remainder; i++) {
widths[i]++;
}
return widths;
}
/**
* Format a table row with proper column widths and truncation
*/
export function formatTableRow(cells, columnWidths, separator = ' ') {
return cells
.map((cell, i) => {
const width = columnWidths[i] || 10;
return truncate(cell ?? '', width).padEnd(width);
})
.join(separator);
}
//# sourceMappingURL=width.js.map