import { cellWidth } from '../cell-width' /** * A line cut into rows of at most `columns` cells as Ink's `wrap` cuts the * built-in's empty state before it centers each row: at spaces, the space a * row breaks on kept at its end, a word wider than a row cut across rows. * * @param text one sanitized line * @param columns the width, at least one cell * @returns the rows, at least one */ export function wrappedLines(text: string, columns: number): string[] { const width = Math.max(1, columns) const rows: string[] = [''] const words = text.split(' ') for (const [index, word] of words.entries()) { const wordWidth = cellWidth(word) let rowWidth = cellWidth(rows[rows.length - 1] ?? '') if (index !== 0) { if (rowWidth >= width) { rows.push('') rowWidth = 0 } rows[rows.length - 1] += ' ' rowWidth += 1 } if (wordWidth > width) { const breaksHere = 1 + Math.floor((wordWidth - (width - rowWidth) - 1) / width) const breaksNext = Math.floor((wordWidth - 1) / width) if (breaksNext < breaksHere) { rows.push('') } const characters = [...word] let visible = cellWidth(rows[rows.length - 1] ?? '') for (const [at, character] of characters.entries()) { const characterWidth = cellWidth(character) if (visible + characterWidth <= width) { rows[rows.length - 1] += character } else { rows.push(character) visible = 0 } visible += characterWidth if (visible === width && at < characters.length - 1) { rows.push('') visible = 0 } } continue } if (rowWidth + wordWidth > width && rowWidth > 0 && wordWidth > 0) { rows.push('') } rows[rows.length - 1] += word } return rows }