@jalasem/dss
Version:
Dev Spaces Switcher (DSS) - Seamlessly manage isolated development environments with separate SSH keys and Git configurations. Enhanced UI with beautiful tables and improved documentation.
303 lines (302 loc) • 17.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UIHelper = void 0;
const chalk_1 = __importDefault(require("chalk"));
const perf_hooks_1 = require("perf_hooks");
class UIHelper {
static success(message) {
console.log(chalk_1.default.green(`✅ ${message}`));
}
static error(message) {
console.log(chalk_1.default.red(`❌ ${message}`));
}
static warning(message) {
console.log(chalk_1.default.yellow(`⚠️ ${message}`));
}
static info(message) {
console.log(chalk_1.default.blue(`ℹ️ ${message}`));
}
static highlight(text) {
return chalk_1.default.cyan(text);
}
static dim(text) {
return chalk_1.default.dim(text);
}
static bold(text) {
return chalk_1.default.bold(text);
}
static activeSpace(name) {
return chalk_1.default.green(`🔥 ${name}`);
}
static inactiveSpace(name) {
return chalk_1.default.white(name);
}
static spaceName(name, isActive = false) {
return isActive ? this.activeSpace(name) : this.inactiveSpace(name);
}
static gradient(text) {
// Create a simple gradient effect using different shades
const colors = [chalk_1.default.blue, chalk_1.default.cyan, chalk_1.default.green, chalk_1.default.yellow];
const chars = text.split('');
return chars.map((char, index) => {
const colorIndex = Math.floor((index / chars.length) * colors.length);
return colors[colorIndex] ? colors[colorIndex](char) : char;
}).join('');
}
static badge(text, type = 'info') {
const colors = {
success: { bg: chalk_1.default.bgGreen, fg: chalk_1.default.black },
error: { bg: chalk_1.default.bgRed, fg: chalk_1.default.white },
warning: { bg: chalk_1.default.bgYellow, fg: chalk_1.default.black },
info: { bg: chalk_1.default.bgBlue, fg: chalk_1.default.white }
};
const { bg, fg } = colors[type];
return bg(fg(` ${text} `));
}
static progressBar(current, total, width = 20) {
const percentage = Math.min(current / total, 1);
const filled = Math.floor(percentage * width);
const empty = width - filled;
const bar = chalk_1.default.green('█'.repeat(filled)) + chalk_1.default.gray('░'.repeat(empty));
const percent = Math.floor(percentage * 100);
return `${bar} ${percent}%`;
}
static command(cmd) {
return chalk_1.default.cyan(`\`${cmd}\``);
}
static filename(path) {
return chalk_1.default.magenta(path);
}
static url(url) {
return chalk_1.default.blue.underline(url);
}
static printSeparator() {
const terminalWidth = process.stdout.columns || 80;
const width = Math.min(60, terminalWidth - 4);
console.log(chalk_1.default.gray('─'.repeat(width)));
}
static printHeader(title) {
const terminalWidth = process.stdout.columns || 80;
const maxWidth = Math.min(Math.max(title.length + 8, 50), terminalWidth - 4);
console.log(chalk_1.default.cyan('╭' + '─'.repeat(maxWidth) + '╮'));
const titleLength = title.length;
const padding = Math.max(0, Math.floor((maxWidth - titleLength) / 2));
const paddedTitle = ' '.repeat(padding) + title + ' '.repeat(maxWidth - titleLength - padding);
console.log(chalk_1.default.cyan('│') + chalk_1.default.bold.white(paddedTitle) + chalk_1.default.cyan('│'));
console.log(chalk_1.default.cyan('╰' + '─'.repeat(maxWidth) + '╯'));
}
// Helper function to get string length without ANSI escape codes
static getDisplayLength(str) {
// Remove ANSI escape codes to get actual display length
// eslint-disable-next-line no-control-regex
return str.replace(/\u001b\[[0-9;]*m/g, '').length;
}
// Helper function to pad string accounting for ANSI escape codes
static padWithColors(str, targetLength) {
const displayLength = this.getDisplayLength(str);
const padding = Math.max(0, targetLength - displayLength);
return str + ' '.repeat(padding);
}
static printSpaceTable(spaces, activeSpace) {
if (spaces.length === 0) {
this.warning('No spaces have been added yet.');
return;
}
const terminalWidth = process.stdout.columns || 80;
const maxTableWidth = Math.min(terminalWidth - 4, 120);
// Calculate column widths with responsive sizing
const minNameWidth = 8;
const minEmailWidth = 12;
const minUserWidth = 8;
const statusWidth = 8;
// Get the actual content lengths
const baseNameWidth = Math.max(minNameWidth, ...spaces.map(s => s.name.length));
const baseEmailWidth = Math.max(minEmailWidth, ...spaces.map(s => s.email.length));
const baseUserWidth = Math.max(minUserWidth, ...spaces.map(s => s.userName.length));
const totalBaseWidth = baseNameWidth + baseEmailWidth + baseUserWidth + statusWidth + 12; // 12 for borders and padding
let nameWidth = baseNameWidth;
let emailWidth = baseEmailWidth;
let userWidth = baseUserWidth;
// Adjust widths if table is too wide for terminal
if (totalBaseWidth > maxTableWidth) {
const availableWidth = maxTableWidth - statusWidth - 12;
const totalContentWidth = baseNameWidth + baseEmailWidth + baseUserWidth;
// Proportionally reduce each column
nameWidth = Math.max(minNameWidth, Math.floor((baseNameWidth / totalContentWidth) * availableWidth));
emailWidth = Math.max(minEmailWidth, Math.floor((baseEmailWidth / totalContentWidth) * availableWidth));
userWidth = Math.max(minUserWidth, availableWidth - nameWidth - emailWidth);
}
// Create border components
const topBorder = `┌${'─'.repeat(nameWidth + 2)}┬${'─'.repeat(emailWidth + 2)}┬${'─'.repeat(userWidth + 2)}┬${'─'.repeat(statusWidth + 2)}┐`;
const separator = `├${'─'.repeat(nameWidth + 2)}┼${'─'.repeat(emailWidth + 2)}┼${'─'.repeat(userWidth + 2)}┼${'─'.repeat(statusWidth + 2)}┤`;
const bottomBorder = `└${'─'.repeat(nameWidth + 2)}┴${'─'.repeat(emailWidth + 2)}┴${'─'.repeat(userWidth + 2)}┴${'─'.repeat(statusWidth + 2)}┘`;
// Print table header
console.log(chalk_1.default.cyan(topBorder));
const headerRow = `│ ${this.padWithColors(chalk_1.default.bold.white('Name'), nameWidth)} │ ${this.padWithColors(chalk_1.default.bold.white('Email'), emailWidth)} │ ${this.padWithColors(chalk_1.default.bold.white('User'), userWidth)} │ ${this.padWithColors(chalk_1.default.bold.white('Status'), statusWidth)} │`;
console.log(headerRow);
console.log(chalk_1.default.cyan(separator));
// Print each space row
spaces.forEach(space => {
const isActive = space.name === activeSpace;
// Truncate long values with ellipsis
const truncatedName = space.name.length > nameWidth ? space.name.substring(0, nameWidth - 3) + '...' : space.name;
const truncatedEmail = space.email.length > emailWidth ? space.email.substring(0, emailWidth - 3) + '...' : space.email;
const truncatedUser = space.userName.length > userWidth ? space.userName.substring(0, userWidth - 3) + '...' : space.userName;
// Apply styling based on active state
const styledName = isActive ? this.activeSpace(truncatedName) : chalk_1.default.white(truncatedName);
const styledEmail = isActive ? chalk_1.default.green(truncatedEmail) : chalk_1.default.gray(truncatedEmail);
const styledUser = isActive ? chalk_1.default.green(truncatedUser) : chalk_1.default.gray(truncatedUser);
const styledStatus = isActive ? this.badge('ACTIVE', 'success') : chalk_1.default.dim('inactive');
const row = `│ ${this.padWithColors(styledName, nameWidth)} │ ${this.padWithColors(styledEmail, emailWidth)} │ ${this.padWithColors(styledUser, userWidth)} │ ${this.padWithColors(styledStatus, statusWidth)} │`;
console.log(row);
});
console.log(chalk_1.default.cyan(bottomBorder));
// Enhanced summary footer with better formatting
const totalSpaces = spaces.length;
const activeCount = activeSpace ? 1 : 0;
const inactiveCount = totalSpaces - activeCount;
console.log('');
console.log(chalk_1.default.dim('📊 Summary:'), chalk_1.default.cyan(`${totalSpaces} total`), chalk_1.default.green(`• ${activeCount} active`), chalk_1.default.gray(`• ${inactiveCount} inactive`));
if (activeSpace) {
console.log('');
console.log(chalk_1.default.dim('🔥 Currently active:'), chalk_1.default.green.bold(activeSpace));
}
console.log('');
console.log(chalk_1.default.dim('Commands:'));
console.log(chalk_1.default.dim(' • '), this.command('dss switch'), chalk_1.default.dim(' - Change active space'));
console.log(chalk_1.default.dim(' • '), this.command('dss inspect <space>'), chalk_1.default.dim(' - View detailed space info'));
console.log(chalk_1.default.dim(' • '), this.command('dss test'), chalk_1.default.dim(' - Test GitHub access'));
console.log('');
}
static printProgress(message) {
this.clearProgress();
this.progressState.active = true;
this.progressState.startTime = perf_hooks_1.performance.now();
this.progressState.message = message;
this.progressState.index = 0;
const updateSpinner = () => {
if (!this.progressState.active)
return;
const elapsed = Math.floor((perf_hooks_1.performance.now() - this.progressState.startTime) / 1000);
const spinner = this.progressState.spinner[this.progressState.index % this.progressState.spinner.length];
const timeStr = elapsed > 0 ? ` (${elapsed}s)` : '';
process.stdout.write(`\r${chalk_1.default.yellow(spinner)} ${this.progressState.message}...${chalk_1.default.dim(timeStr)}`);
this.progressState.index++;
};
updateSpinner();
this.progressState.interval = setInterval(updateSpinner, 80);
}
static clearProgress() {
if (this.progressState.interval) {
clearInterval(this.progressState.interval);
this.progressState.interval = null;
}
this.progressState.active = false;
const terminalWidth = process.stdout.columns || 80;
process.stdout.write('\r' + ' '.repeat(terminalWidth) + '\r');
}
static updateProgress(message) {
if (this.progressState.active) {
this.progressState.message = message;
}
}
static printKeyInstruction() {
console.log(chalk_1.default.dim('\n💡 Tips:'));
console.log(chalk_1.default.dim(' • Use arrow keys to navigate'));
console.log(chalk_1.default.dim(' • Press Enter to select'));
console.log(chalk_1.default.dim(' • Press Ctrl+C to cancel'));
}
static printSuccessBox(title, content) {
const maxWidth = Math.max(title.length, ...content.map(line => line.length));
const terminalWidth = process.stdout.columns || 80;
const width = Math.min(Math.max(maxWidth + 4, 40), terminalWidth - 4);
console.log(chalk_1.default.green('╔' + '═'.repeat(width - 2) + '╗'));
console.log(chalk_1.default.green('║') + chalk_1.default.green.bold(title.padStart((width + title.length) / 2).padEnd(width - 2)) + chalk_1.default.green('║'));
console.log(chalk_1.default.green('╠' + '═'.repeat(width - 2) + '╣'));
content.forEach(line => {
const truncatedLine = line.length > width - 4 ? line.substring(0, width - 7) + '...' : line;
console.log(chalk_1.default.green('║') + ` ${truncatedLine}`.padEnd(width - 2) + chalk_1.default.green('║'));
});
console.log(chalk_1.default.green('╚' + '═'.repeat(width - 2) + '╝'));
}
static printErrorBox(title, content) {
const maxWidth = Math.max(title.length, ...content.map(line => line.length));
const terminalWidth = process.stdout.columns || 80;
const width = Math.min(Math.max(maxWidth + 4, 40), terminalWidth - 4);
console.log(chalk_1.default.red('╔' + '═'.repeat(width - 2) + '╗'));
console.log(chalk_1.default.red('║') + chalk_1.default.red.bold(title.padStart((width + title.length) / 2).padEnd(width - 2)) + chalk_1.default.red('║'));
console.log(chalk_1.default.red('╠' + '═'.repeat(width - 2) + '╣'));
content.forEach(line => {
const truncatedLine = line.length > width - 4 ? line.substring(0, width - 7) + '...' : line;
console.log(chalk_1.default.red('║') + ` ${truncatedLine}`.padEnd(width - 2) + chalk_1.default.red('║'));
});
console.log(chalk_1.default.red('╚' + '═'.repeat(width - 2) + '╝'));
}
static printInfoBox(title, content) {
const maxWidth = Math.max(title.length, ...content.map(line => line.length));
const terminalWidth = process.stdout.columns || 80;
const width = Math.min(Math.max(maxWidth + 4, 40), terminalWidth - 4);
console.log(chalk_1.default.blue('╔' + '═'.repeat(width - 2) + '╗'));
console.log(chalk_1.default.blue('║') + chalk_1.default.blue.bold(title.padStart((width + title.length) / 2).padEnd(width - 2)) + chalk_1.default.blue('║'));
console.log(chalk_1.default.blue('╠' + '═'.repeat(width - 2) + '╣'));
content.forEach(line => {
const truncatedLine = line.length > width - 4 ? line.substring(0, width - 7) + '...' : line;
console.log(chalk_1.default.blue('║') + ` ${truncatedLine}`.padEnd(width - 2) + chalk_1.default.blue('║'));
});
console.log(chalk_1.default.blue('╚' + '═'.repeat(width - 2) + '╝'));
}
static printStatus(label, value, status = 'info') {
const statusIcon = {
success: '✅',
error: '❌',
warning: '⚠️',
info: 'ℹ️'
}[status];
const statusColor = {
success: chalk_1.default.green,
error: chalk_1.default.red,
warning: chalk_1.default.yellow,
info: chalk_1.default.blue
}[status];
console.log(`${statusIcon} ${chalk_1.default.bold(label)}: ${statusColor(value)}`);
}
static printWelcome() {
const terminalWidth = process.stdout.columns || 80;
const maxWidth = Math.min(70, terminalWidth - 4);
console.log('');
console.log(chalk_1.default.cyan('╭' + '═'.repeat(maxWidth) + '╮'));
console.log(chalk_1.default.cyan('║') + chalk_1.default.bold.cyan(' '.repeat(Math.floor((maxWidth - 24) / 2)) + '🚀 Dev Spaces Switcher' + ' '.repeat(Math.ceil((maxWidth - 24) / 2))) + chalk_1.default.cyan('║'));
console.log(chalk_1.default.cyan('║') + chalk_1.default.dim(' '.repeat(Math.floor((maxWidth - 52) / 2)) + 'Manage isolated development environments with ease' + ' '.repeat(Math.ceil((maxWidth - 52) / 2))) + chalk_1.default.cyan('║'));
console.log(chalk_1.default.cyan('╰' + '═'.repeat(maxWidth) + '╯'));
console.log('');
}
static printQuickHelp() {
console.log(chalk_1.default.dim('Quick commands:'));
console.log(chalk_1.default.dim(' • '), this.command('dss list'), chalk_1.default.dim(' - Show all spaces'));
console.log(chalk_1.default.dim(' • '), this.command('dss add'), chalk_1.default.dim(' - Add new space'));
console.log(chalk_1.default.dim(' • '), this.command('dss switch'), chalk_1.default.dim(' - Switch between spaces'));
console.log(chalk_1.default.dim(' • '), this.command('dss --help'), chalk_1.default.dim(' - Show detailed help'));
console.log('');
}
static printSpaceSwitched(spaceName) {
console.log('');
console.log(chalk_1.default.green('╭─────────────────────────────────────╮'));
console.log(chalk_1.default.green('│') + chalk_1.default.green.bold(' ✨ Space Switched Successfully! ') + chalk_1.default.green('│'));
console.log(chalk_1.default.green('├─────────────────────────────────────┤'));
console.log(chalk_1.default.green('│') + chalk_1.default.white(` Active space: ${chalk_1.default.green.bold(spaceName)}`.padEnd(35)) + chalk_1.default.green('│'));
console.log(chalk_1.default.green('╰─────────────────────────────────────╯'));
console.log('');
}
}
exports.UIHelper = UIHelper;
UIHelper.progressState = {
active: false,
startTime: 0,
message: '',
spinner: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧'],
index: 0,
interval: null
};