CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
index.js403 linesDownload Raw Back to boxen
1import process from 'node:process';2import stringWidth from 'string-width';3import chalk from 'chalk';4import widestLine from 'widest-line';5import cliBoxes from 'cli-boxes';6import camelCase from 'camelcase';7import ansiAlign from 'ansi-align';8import wrapAnsi from 'wrap-ansi';9 10const NEWLINE = '\n';11const PAD = ' ';12const NONE = 'none';13 14const terminalColumns = () => {15	const {env, stdout, stderr} = process;16 17	if (stdout?.columns) {18		return stdout.columns;19	}20 21	if (stderr?.columns) {22		return stderr.columns;23	}24 25	if (env.COLUMNS) {26		return Number.parseInt(env.COLUMNS, 10);27	}28 29	return 80;30};31 32const getObject = detail => typeof detail === 'number' ? {33	top: detail,34	right: detail * 3,35	bottom: detail,36	left: detail * 3,37} : {38	top: 0,39	right: 0,40	bottom: 0,41	left: 0,42	...detail,43};44 45const getBorderWidth = borderStyle => borderStyle === NONE ? 0 : 2;46 47const getBorderChars = borderStyle => {48	const sides = [49		'topLeft',50		'topRight',51		'bottomRight',52		'bottomLeft',53		'left',54		'right',55		'top',56		'bottom',57	];58 59	let characters;60 61	// Create empty border style62	if (borderStyle === NONE) {63		borderStyle = {};64		for (const side of sides) {65			borderStyle[side] = '';66		}67	}68 69	if (typeof borderStyle === 'string') {70		characters = cliBoxes[borderStyle];71 72		if (!characters) {73			throw new TypeError(`Invalid border style: ${borderStyle}`);74		}75	} else {76		// Ensure retro-compatibility77		if (typeof borderStyle?.vertical === 'string') {78			borderStyle.left = borderStyle.vertical;79			borderStyle.right = borderStyle.vertical;80		}81 82		// Ensure retro-compatibility83		if (typeof borderStyle?.horizontal === 'string') {84			borderStyle.top = borderStyle.horizontal;85			borderStyle.bottom = borderStyle.horizontal;86		}87 88		for (const side of sides) {89			if (borderStyle[side] === null || typeof borderStyle[side] !== 'string') {90				throw new TypeError(`Invalid border style: ${side}`);91			}92		}93 94		characters = borderStyle;95	}96 97	return characters;98};99 100const makeTitle = (text, horizontal, alignment) => {101	let title = '';102 103	const textWidth = stringWidth(text);104 105	switch (alignment) {106		case 'left': {107			title = text + horizontal.slice(textWidth);108			break;109		}110 111		case 'right': {112			title = horizontal.slice(textWidth) + text;113			break;114		}115 116		default: {117			horizontal = horizontal.slice(textWidth);118 119			if (horizontal.length % 2 === 1) { // This is needed in case the length is odd120				horizontal = horizontal.slice(Math.floor(horizontal.length / 2));121				title = horizontal.slice(1) + text + horizontal; // We reduce the left part of one character to avoid the bar to go beyond its limit122			} else {123				horizontal = horizontal.slice(horizontal.length / 2);124				title = horizontal + text + horizontal;125			}126 127			break;128		}129	}130 131	return title;132};133 134const makeContentText = (text, {padding, width, textAlignment, height}) => {135	text = ansiAlign(text, {align: textAlignment});136	let lines = text.split(NEWLINE);137	const textWidth = widestLine(text);138 139	const max = width - padding.left - padding.right;140 141	if (textWidth > max) {142		const newLines = [];143		for (const line of lines) {144			const createdLines = wrapAnsi(line, max, {hard: true});145			const alignedLines = ansiAlign(createdLines, {align: textAlignment});146			const alignedLinesArray = alignedLines.split('\n');147			const longestLength = Math.max(...alignedLinesArray.map(s => stringWidth(s)));148 149			for (const alignedLine of alignedLinesArray) {150				let paddedLine;151				switch (textAlignment) {152					case 'center': {153						paddedLine = PAD.repeat((max - longestLength) / 2) + alignedLine;154						break;155					}156 157					case 'right': {158						paddedLine = PAD.repeat(max - longestLength) + alignedLine;159						break;160					}161 162					default: {163						paddedLine = alignedLine;164						break;165					}166				}167 168				newLines.push(paddedLine);169			}170		}171 172		lines = newLines;173	}174 175	if (textAlignment === 'center' && textWidth < max) {176		lines = lines.map(line => PAD.repeat((max - textWidth) / 2) + line);177	} else if (textAlignment === 'right' && textWidth < max) {178		lines = lines.map(line => PAD.repeat(max - textWidth) + line);179	}180 181	const paddingLeft = PAD.repeat(padding.left);182	const paddingRight = PAD.repeat(padding.right);183 184	lines = lines.map(line => paddingLeft + line + paddingRight);185 186	lines = lines.map(line => {187		if (width - stringWidth(line) > 0) {188			switch (textAlignment) {189				case 'center': {190					return line + PAD.repeat(width - stringWidth(line));191				}192 193				case 'right': {194					return line + PAD.repeat(width - stringWidth(line));195				}196 197				default: {198					return line + PAD.repeat(width - stringWidth(line));199				}200			}201		}202 203		return line;204	});205 206	if (padding.top > 0) {207		lines = [...Array.from({length: padding.top}).fill(PAD.repeat(width)), ...lines];208	}209 210	if (padding.bottom > 0) {211		lines = [...lines, ...Array.from({length: padding.bottom}).fill(PAD.repeat(width))];212	}213 214	if (height && lines.length > height) {215		lines = lines.slice(0, height);216	} else if (height && lines.length < height) {217		lines = [...lines, ...Array.from({length: height - lines.length}).fill(PAD.repeat(width))];218	}219 220	return lines.join(NEWLINE);221};222 223const boxContent = (content, contentWidth, options) => {224	const colorizeBorder = border => {225		const newBorder = options.borderColor ? getColorFn(options.borderColor)(border) : border;226		return options.dimBorder ? chalk.dim(newBorder) : newBorder;227	};228 229	const colorizeContent = content => options.backgroundColor ? getBGColorFn(options.backgroundColor)(content) : content;230 231	const chars = getBorderChars(options.borderStyle);232	const columns = terminalColumns();233	let marginLeft = PAD.repeat(options.margin.left);234 235	if (options.float === 'center') {236		const marginWidth = Math.max((columns - contentWidth - getBorderWidth(options.borderStyle)) / 2, 0);237		marginLeft = PAD.repeat(marginWidth);238	} else if (options.float === 'right') {239		const marginWidth = Math.max(columns - contentWidth - options.margin.right - getBorderWidth(options.borderStyle), 0);240		marginLeft = PAD.repeat(marginWidth);241	}242 243	let result = '';244 245	if (options.margin.top) {246		result += NEWLINE.repeat(options.margin.top);247	}248 249	if (options.borderStyle !== NONE || options.title) {250		result += colorizeBorder(marginLeft + chars.topLeft + (options.title ? makeTitle(options.title, chars.top.repeat(contentWidth), options.titleAlignment) : chars.top.repeat(contentWidth)) + chars.topRight) + NEWLINE;251	}252 253	const lines = content.split(NEWLINE);254 255	result += lines.map(line => marginLeft + colorizeBorder(chars.left) + colorizeContent(line) + colorizeBorder(chars.right)).join(NEWLINE);256 257	if (options.borderStyle !== NONE) {258		result += NEWLINE + colorizeBorder(marginLeft + chars.bottomLeft + chars.bottom.repeat(contentWidth) + chars.bottomRight);259	}260 261	if (options.margin.bottom) {262		result += NEWLINE.repeat(options.margin.bottom);263	}264 265	return result;266};267 268const sanitizeOptions = options => {269	// If fullscreen is enabled, max-out unspecified width/height270	if (options.fullscreen && process?.stdout) {271		let newDimensions = [process.stdout.columns, process.stdout.rows];272 273		if (typeof options.fullscreen === 'function') {274			newDimensions = options.fullscreen(...newDimensions);275		}276 277		if (!options.width) {278			options.width = newDimensions[0];279		}280 281		if (!options.height) {282			options.height = newDimensions[1];283		}284	}285 286	// If width is provided, make sure it's not below 1287	if (options.width) {288		options.width = Math.max(1, options.width - getBorderWidth(options.borderStyle));289	}290 291	// If height is provided, make sure it's not below 1292	if (options.height) {293		options.height = Math.max(1, options.height - getBorderWidth(options.borderStyle));294	}295 296	return options;297};298 299const formatTitle = (title, borderStyle) => borderStyle === NONE ? title : ` ${title} `;300 301const determineDimensions = (text, options) => {302	options = sanitizeOptions(options);303	const widthOverride = options.width !== undefined;304	const columns = terminalColumns();305	const borderWidth = getBorderWidth(options.borderStyle);306	const maxWidth = columns - options.margin.left - options.margin.right - borderWidth;307 308	const widest = widestLine(wrapAnsi(text, columns - borderWidth, {hard: true, trim: false})) + options.padding.left + options.padding.right;309 310	// If title and width are provided, title adheres to fixed width311	if (options.title && widthOverride) {312		options.title = options.title.slice(0, Math.max(0, options.width - 2));313		if (options.title) {314			options.title = formatTitle(options.title, options.borderStyle);315		}316	} else if (options.title) {317		options.title = options.title.slice(0, Math.max(0, maxWidth - 2));318 319		// Recheck if title isn't empty now320		if (options.title) {321			options.title = formatTitle(options.title, options.borderStyle);322			// If the title is larger than content, box adheres to title width323			if (stringWidth(options.title) > widest) {324				options.width = stringWidth(options.title);325			}326		}327	}328 329	// If fixed width is provided, use it or content width as reference330	options.width = options.width ? options.width : widest;331 332	if (!widthOverride) {333		if ((options.margin.left && options.margin.right) && options.width > maxWidth) {334			// Let's assume we have margins: left = 3, right = 5, in total = 8335			const spaceForMargins = columns - options.width - borderWidth;336			// Let's assume we have space = 4337			const multiplier = spaceForMargins / (options.margin.left + options.margin.right);338			// Here: multiplier = 4/8 = 0.5339			options.margin.left = Math.max(0, Math.floor(options.margin.left * multiplier));340			options.margin.right = Math.max(0, Math.floor(options.margin.right * multiplier));341			// Left: 3 * 0.5 = 1.5 -> 1342			// Right: 6 * 0.5 = 3343		}344 345		// Re-cap width considering the margins after shrinking346		options.width = Math.min(options.width, columns - borderWidth - options.margin.left - options.margin.right);347	}348 349	// Prevent padding overflow350	if (options.width - (options.padding.left + options.padding.right) <= 0) {351		options.padding.left = 0;352		options.padding.right = 0;353	}354 355	if (options.height && options.height - (options.padding.top + options.padding.bottom) <= 0) {356		options.padding.top = 0;357		options.padding.bottom = 0;358	}359 360	return options;361};362 363const isHex = color => color.match(/^#(?:[0-f]{3}){1,2}$/i);364const isColorValid = color => typeof color === 'string' && (chalk[color] ?? isHex(color));365const getColorFn = color => isHex(color) ? chalk.hex(color) : chalk[color];366const getBGColorFn = color => isHex(color) ? chalk.bgHex(color) : chalk[camelCase(['bg', color])];367 368export default function boxen(text, options) {369	options = {370		padding: 0,371		borderStyle: 'single',372		dimBorder: false,373		textAlignment: 'left',374		float: 'left',375		titleAlignment: 'left',376		...options,377	};378 379	// This option is deprecated380	if (options.align) {381		options.textAlignment = options.align;382	}383 384	if (options.borderColor && !isColorValid(options.borderColor)) {385		throw new Error(`${options.borderColor} is not a valid borderColor`);386	}387 388	if (options.backgroundColor && !isColorValid(options.backgroundColor)) {389		throw new Error(`${options.backgroundColor} is not a valid backgroundColor`);390	}391 392	options.padding = getObject(options.padding);393	options.margin = getObject(options.margin);394 395	options = determineDimensions(text, options);396 397	text = makeContentText(text, options);398 399	return boxContent(text, options.width, options);400}401 402export {default as _borderStyles} from 'cli-boxes';403 
basant307/AI_Governance_Project · CoolFace