├── .editorconfig ├── .gitattributes ├── .github ├── security.md └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── example.js ├── index.d.ts ├── index.js ├── index.test-d.ts ├── license ├── package.json ├── readme.md ├── screenshot.png └── tests ├── background-option.js ├── border-option.js ├── float-option.js ├── fullscreen-option.js ├── height-option.js ├── main.js ├── margin-option.js ├── padding-option.js ├── snapshots └── tests │ ├── background-option.js.md │ ├── background-option.js.snap │ ├── border-option.js.md │ ├── border-option.js.snap │ ├── float-option.js.md │ ├── float-option.js.snap │ ├── fullscreen-option.js.md │ ├── fullscreen-option.js.snap │ ├── height-option.js.md │ ├── height-option.js.snap │ ├── main.js.md │ ├── main.js.snap │ ├── margin-option.js.md │ ├── margin-option.js.snap │ ├── padding-option.js.md │ ├── padding-option.js.snap │ ├── text-align-option.js.md │ ├── text-align-option.js.snap │ ├── title-option.js.md │ ├── title-option.js.snap │ ├── width-option.js.md │ └── width-option.js.snap ├── text-align-option.js ├── title-option.js └── width-option.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/security.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 22 14 | - 20 15 | - 18 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | - run: npm install 22 | - shell: 'script -q -e -c "bash {0}"' # Workaround for https://github.com/actions/runner/issues/241 23 | run: npm test 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import chalk from 'chalk'; 3 | import boxen from './index.js'; 4 | 5 | console.log('\n\n' + boxen(chalk.cyan('unicorn'), { 6 | padding: 1, 7 | margin: 1, 8 | borderColor: 'yellow', 9 | }) + '\n'); 10 | 11 | console.log('\n\n' + boxen(chalk.cyan('unicorn'), { 12 | padding: 1, 13 | margin: 1, 14 | borderColor: 'yellow', 15 | borderStyle: 'double', 16 | }) + '\n'); 17 | 18 | console.log('\n\n' + boxen(chalk.cyan('unicorn'), { 19 | padding: 1, 20 | margin: 1, 21 | borderColor: '#eebbaa', 22 | borderStyle: 'double', 23 | }) + '\n'); 24 | 25 | console.log('\n\n' + boxen(chalk.black('unicorn'), { 26 | padding: 1, 27 | margin: 1, 28 | borderColor: '#ffc0cb', 29 | backgroundColor: '#00ffff', 30 | borderStyle: 'double', 31 | }) + '\n'); 32 | 33 | console.log('\n\n' + boxen(chalk.black('unicorn'), { 34 | padding: 1, 35 | margin: 1, 36 | borderColor: 'yellow', 37 | backgroundColor: 'magenta', 38 | borderStyle: { 39 | topLeft: '+', 40 | topRight: '+', 41 | bottomLeft: '+', 42 | bottomRight: '+', 43 | top: '-', 44 | bottom: '-', 45 | left: '|', 46 | right: '|', 47 | }, 48 | }) + '\n'); 49 | 50 | const sentences = 'Unbreakable_text_because_it_has_no_spaces '.repeat(5); 51 | console.log('\n\n' + boxen(sentences, {textAlignment: 'left'}) + '\n'); 52 | 53 | console.log('\n\n' + boxen(sentences, {textAlignment: 'center'}) + '\n'); 54 | 55 | console.log('\n\n' + boxen(sentences, { 56 | textAlignment: 'right', 57 | padding: { 58 | left: 1, 59 | right: 1, 60 | top: 0, 61 | bottom: 0, 62 | }, 63 | }) + '\n'); 64 | 65 | const longWord = 'x'.repeat(process.stdout.columns + 20); 66 | console.log('\n\n' + boxen(longWord, {textAlignment: 'center'}) + '\n'); 67 | 68 | const title = 'Beautiful title'; 69 | console.log('\n\n' + boxen('This box has a nice title', {title}) + '\n'); 70 | 71 | console.log('\n\n' + boxen('This box has a centered title', {title, titleAlignment: 'center'}) + '\n'); 72 | 73 | console.log('\n\n' + boxen('This box has fixed width of 20', {width: 20}) + '\n'); 74 | 75 | console.log('\n\n' + boxen('This box has fixed width of 50', {width: 50}) + '\n'); 76 | 77 | console.log('\n\n' + boxen('This box has fixed height of 5', {height: 5}) + '\n'); 78 | 79 | console.log('\n\n' + boxen('This box has fixed height of 5', {height: 5, padding: 2}) + '\n'); 80 | 81 | console.log('\n\n' + boxen('This box has fixed height of 5 and width of 15', {height: 8, width: 15}) + '\n'); 82 | 83 | console.log('\n\n' + boxen('This box is in fullscreen !', {fullscreen: true}) + '\n'); 84 | 85 | console.log('\n\n' + boxen('This box is in full-width and half-height !', {fullscreen: (w, h) => [w, h / 2]}) + '\n'); 86 | 87 | console.log('\n\n' + boxen('And this one is in half-width and full-height !', {fullscreen: (w, h) => [w / 2, h]}) + '\n'); 88 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import {type LiteralUnion} from 'type-fest'; 2 | import {type BoxStyle, type Boxes as CLIBoxes} from 'cli-boxes'; 3 | 4 | /** 5 | All box styles. 6 | */ 7 | type Boxes = { 8 | readonly none: BoxStyle; 9 | } & CLIBoxes; 10 | 11 | /** 12 | Characters used for custom border. 13 | 14 | @example 15 | ``` 16 | // attttb 17 | // l r 18 | // dbbbbc 19 | 20 | const border: CustomBorderStyle = { 21 | topLeft: 'a', 22 | topRight: 'b', 23 | bottomRight: 'c', 24 | bottomLeft: 'd', 25 | left: 'l', 26 | right: 'r', 27 | top: 't', 28 | bottom: 'b', 29 | }; 30 | ``` 31 | */ 32 | export type CustomBorderStyle = { 33 | /** 34 | @deprecated Use `top` and `bottom` instead. 35 | */ 36 | horizontal?: string; 37 | 38 | /** 39 | @deprecated Use `left` and `right` instead. 40 | */ 41 | vertical?: string; 42 | } & BoxStyle; 43 | 44 | /** 45 | Spacing used for `padding` and `margin`. 46 | */ 47 | export type Spacing = { 48 | readonly top?: number; 49 | readonly right?: number; 50 | readonly bottom?: number; 51 | readonly left?: number; 52 | }; 53 | 54 | export type Options = { 55 | /** 56 | Color of the box border. 57 | */ 58 | readonly borderColor?: LiteralUnion< 59 | | 'black' 60 | | 'red' 61 | | 'green' 62 | | 'yellow' 63 | | 'blue' 64 | | 'magenta' 65 | | 'cyan' 66 | | 'white' 67 | | 'gray' 68 | | 'grey' 69 | | 'blackBright' 70 | | 'redBright' 71 | | 'greenBright' 72 | | 'yellowBright' 73 | | 'blueBright' 74 | | 'magentaBright' 75 | | 'cyanBright' 76 | | 'whiteBright', 77 | string 78 | >; 79 | 80 | /** 81 | Style of the box border. 82 | 83 | @default 'single' 84 | */ 85 | readonly borderStyle?: keyof Boxes | CustomBorderStyle; 86 | 87 | /** 88 | Reduce opacity of the border. 89 | 90 | @default false 91 | */ 92 | readonly dimBorder?: boolean; 93 | 94 | /** 95 | Space between the text and box border. 96 | 97 | @default 0 98 | */ 99 | readonly padding?: number | Spacing; 100 | 101 | /** 102 | Space around the box. 103 | 104 | @default 0 105 | */ 106 | readonly margin?: number | Spacing; 107 | 108 | /** 109 | Float the box on the available terminal screen space. 110 | 111 | @default 'left' 112 | */ 113 | readonly float?: 'left' | 'right' | 'center'; 114 | 115 | /** 116 | Color of the background. 117 | */ 118 | readonly backgroundColor?: LiteralUnion< 119 | | 'black' 120 | | 'red' 121 | | 'green' 122 | | 'yellow' 123 | | 'blue' 124 | | 'magenta' 125 | | 'cyan' 126 | | 'white' 127 | | 'blackBright' 128 | | 'redBright' 129 | | 'greenBright' 130 | | 'yellowBright' 131 | | 'blueBright' 132 | | 'magentaBright' 133 | | 'cyanBright' 134 | | 'whiteBright', 135 | string 136 | >; 137 | 138 | /** 139 | Align the text in the box based on the widest line. 140 | 141 | @default 'left' 142 | @deprecated Use `textAlignment` instead. 143 | */ 144 | readonly align?: 'left' | 'right' | 'center'; 145 | 146 | /** 147 | Align the text in the box based on the widest line. 148 | 149 | @default 'left' 150 | */ 151 | readonly textAlignment?: 'left' | 'right' | 'center'; 152 | 153 | /** 154 | Display a title at the top of the box. 155 | If needed, the box will horizontally expand to fit the title. 156 | 157 | @example 158 | ``` 159 | console.log(boxen('foo bar', {title: 'example'})); 160 | // ┌ example ┐ 161 | // │foo bar │ 162 | // └─────────┘ 163 | ``` 164 | */ 165 | readonly title?: string; 166 | 167 | /** 168 | Align the title in the top bar. 169 | 170 | @default 'left' 171 | 172 | @example 173 | ``` 174 | console.log(boxen('foo bar foo bar', {title: 'example', titleAlignment: 'center'})); 175 | // ┌─── example ───┐ 176 | // │foo bar foo bar│ 177 | // └───────────────┘ 178 | 179 | console.log(boxen('foo bar foo bar', {title: 'example', titleAlignment: 'right'})); 180 | // ┌────── example ┐ 181 | // │foo bar foo bar│ 182 | // └───────────────┘ 183 | ``` 184 | */ 185 | readonly titleAlignment?: 'left' | 'right' | 'center'; 186 | 187 | /** 188 | Set a fixed width for the box. 189 | 190 | __Note__: This disables terminal overflow handling and may cause the box to look broken if the user's terminal is not wide enough. 191 | 192 | @example 193 | ``` 194 | import boxen from 'boxen'; 195 | 196 | console.log(boxen('foo bar', {width: 15})); 197 | // ┌─────────────┐ 198 | // │foo bar │ 199 | // └─────────────┘ 200 | ``` 201 | */ 202 | readonly width?: number; 203 | 204 | /** 205 | Set a fixed height for the box. 206 | 207 | __Note__: This option will crop overflowing content. 208 | 209 | @example 210 | ``` 211 | import boxen from 'boxen'; 212 | 213 | console.log(boxen('foo bar', {height: 5})); 214 | // ┌───────┐ 215 | // │foo bar│ 216 | // │ │ 217 | // │ │ 218 | // └───────┘ 219 | ``` 220 | */ 221 | readonly height?: number; 222 | 223 | /** 224 | __boolean__: Whether or not to fit all available space within the terminal. 225 | 226 | __function__: Pass a callback function to control box dimensions. 227 | 228 | @example 229 | ``` 230 | import boxen from 'boxen'; 231 | 232 | console.log(boxen('foo bar', { 233 | fullscreen: (width, height) => [width, height - 1], 234 | })); 235 | ``` 236 | */ 237 | readonly fullscreen?: boolean | ((width: number, height: number) => [width: number, height: number]); 238 | }; 239 | 240 | /** 241 | Creates a box in the terminal. 242 | 243 | @param text - The text inside the box. 244 | @returns The box. 245 | 246 | @example 247 | ``` 248 | import boxen from 'boxen'; 249 | 250 | console.log(boxen('unicorn', {padding: 1})); 251 | // ┌─────────────┐ 252 | // │ │ 253 | // │ unicorn │ 254 | // │ │ 255 | // └─────────────┘ 256 | 257 | console.log(boxen('unicorn', {padding: 1, margin: 1, borderStyle: 'double'})); 258 | // 259 | // ╔═════════════╗ 260 | // ║ ║ 261 | // ║ unicorn ║ 262 | // ║ ║ 263 | // ╚═════════════╝ 264 | // 265 | ``` 266 | */ 267 | export default function boxen(text: string, options?: Options): string; 268 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import stringWidth from 'string-width'; 3 | import chalk from 'chalk'; 4 | import widestLine from 'widest-line'; 5 | import cliBoxes from 'cli-boxes'; 6 | import camelCase from 'camelcase'; 7 | import ansiAlign from 'ansi-align'; 8 | import wrapAnsi from 'wrap-ansi'; 9 | 10 | const NEWLINE = '\n'; 11 | const PAD = ' '; 12 | const NONE = 'none'; 13 | 14 | const 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 | 32 | const 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 | 45 | const getBorderWidth = borderStyle => borderStyle === NONE ? 0 : 2; 46 | 47 | const 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 style 62 | 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-compatibility 77 | if (typeof borderStyle?.vertical === 'string') { 78 | borderStyle.left = borderStyle.vertical; 79 | borderStyle.right = borderStyle.vertical; 80 | } 81 | 82 | // Ensure retro-compatibility 83 | 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 | 100 | const 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 odd 120 | 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 limit 122 | } 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 | 134 | const 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 => { 185 | const newLine = paddingLeft + line + paddingRight; 186 | 187 | return newLine + PAD.repeat(width - stringWidth(newLine)); 188 | }); 189 | 190 | if (padding.top > 0) { 191 | lines = [...Array.from({length: padding.top}).fill(PAD.repeat(width)), ...lines]; 192 | } 193 | 194 | if (padding.bottom > 0) { 195 | lines = [...lines, ...Array.from({length: padding.bottom}).fill(PAD.repeat(width))]; 196 | } 197 | 198 | if (height && lines.length > height) { 199 | lines = lines.slice(0, height); 200 | } else if (height && lines.length < height) { 201 | lines = [...lines, ...Array.from({length: height - lines.length}).fill(PAD.repeat(width))]; 202 | } 203 | 204 | return lines.join(NEWLINE); 205 | }; 206 | 207 | const boxContent = (content, contentWidth, options) => { 208 | const colorizeBorder = border => { 209 | const newBorder = options.borderColor ? getColorFunction(options.borderColor)(border) : border; 210 | return options.dimBorder ? chalk.dim(newBorder) : newBorder; 211 | }; 212 | 213 | const colorizeContent = content => options.backgroundColor ? getBGColorFunction(options.backgroundColor)(content) : content; 214 | 215 | const chars = getBorderChars(options.borderStyle); 216 | const columns = terminalColumns(); 217 | let marginLeft = PAD.repeat(options.margin.left); 218 | 219 | if (options.float === 'center') { 220 | const marginWidth = Math.max((columns - contentWidth - getBorderWidth(options.borderStyle)) / 2, 0); 221 | marginLeft = PAD.repeat(marginWidth); 222 | } else if (options.float === 'right') { 223 | const marginWidth = Math.max(columns - contentWidth - options.margin.right - getBorderWidth(options.borderStyle), 0); 224 | marginLeft = PAD.repeat(marginWidth); 225 | } 226 | 227 | let result = ''; 228 | 229 | if (options.margin.top) { 230 | result += NEWLINE.repeat(options.margin.top); 231 | } 232 | 233 | if (options.borderStyle !== NONE || options.title) { 234 | result += colorizeBorder(marginLeft + chars.topLeft + (options.title ? makeTitle(options.title, chars.top.repeat(contentWidth), options.titleAlignment) : chars.top.repeat(contentWidth)) + chars.topRight) + NEWLINE; 235 | } 236 | 237 | const lines = content.split(NEWLINE); 238 | 239 | result += lines.map(line => marginLeft + colorizeBorder(chars.left) + colorizeContent(line) + colorizeBorder(chars.right)).join(NEWLINE); 240 | 241 | if (options.borderStyle !== NONE) { 242 | result += NEWLINE + colorizeBorder(marginLeft + chars.bottomLeft + chars.bottom.repeat(contentWidth) + chars.bottomRight); 243 | } 244 | 245 | if (options.margin.bottom) { 246 | result += NEWLINE.repeat(options.margin.bottom); 247 | } 248 | 249 | return result; 250 | }; 251 | 252 | const sanitizeOptions = options => { 253 | // If fullscreen is enabled, max-out unspecified width/height 254 | if (options.fullscreen && process?.stdout) { 255 | let newDimensions = [process.stdout.columns, process.stdout.rows]; 256 | 257 | if (typeof options.fullscreen === 'function') { 258 | newDimensions = options.fullscreen(...newDimensions); 259 | } 260 | 261 | options.width ||= newDimensions[0]; 262 | 263 | options.height ||= newDimensions[1]; 264 | } 265 | 266 | // If width is provided, make sure it's not below 1 267 | options.width &&= Math.max(1, options.width - getBorderWidth(options.borderStyle)); 268 | 269 | // If height is provided, make sure it's not below 1 270 | options.height &&= Math.max(1, options.height - getBorderWidth(options.borderStyle)); 271 | 272 | return options; 273 | }; 274 | 275 | const formatTitle = (title, borderStyle) => borderStyle === NONE ? title : ` ${title} `; 276 | 277 | const determineDimensions = (text, options) => { 278 | options = sanitizeOptions(options); 279 | const widthOverride = options.width !== undefined; 280 | const columns = terminalColumns(); 281 | const borderWidth = getBorderWidth(options.borderStyle); 282 | const maxWidth = columns - options.margin.left - options.margin.right - borderWidth; 283 | 284 | const widest = widestLine(wrapAnsi(text, columns - borderWidth, {hard: true, trim: false})) + options.padding.left + options.padding.right; 285 | 286 | // If title and width are provided, title adheres to fixed width 287 | if (options.title && widthOverride) { 288 | options.title = options.title.slice(0, Math.max(0, options.width - 2)); 289 | options.title &&= formatTitle(options.title, options.borderStyle); 290 | } else if (options.title) { 291 | options.title = options.title.slice(0, Math.max(0, maxWidth - 2)); 292 | 293 | // Recheck if title isn't empty now 294 | if (options.title) { 295 | options.title = formatTitle(options.title, options.borderStyle); 296 | // If the title is larger than content, box adheres to title width 297 | if (stringWidth(options.title) > widest) { 298 | options.width = stringWidth(options.title); 299 | } 300 | } 301 | } 302 | 303 | // If fixed width is provided, use it or content width as reference 304 | options.width ||= widest; 305 | 306 | if (!widthOverride) { 307 | if ((options.margin.left && options.margin.right) && options.width > maxWidth) { 308 | // Let's assume we have margins: left = 3, right = 5, in total = 8 309 | const spaceForMargins = columns - options.width - borderWidth; 310 | // Let's assume we have space = 4 311 | const multiplier = spaceForMargins / (options.margin.left + options.margin.right); 312 | // Here: multiplier = 4/8 = 0.5 313 | options.margin.left = Math.max(0, Math.floor(options.margin.left * multiplier)); 314 | options.margin.right = Math.max(0, Math.floor(options.margin.right * multiplier)); 315 | // Left: 3 * 0.5 = 1.5 -> 1 316 | // Right: 6 * 0.5 = 3 317 | } 318 | 319 | // Re-cap width considering the margins after shrinking 320 | options.width = Math.min(options.width, columns - borderWidth - options.margin.left - options.margin.right); 321 | } 322 | 323 | // Prevent padding overflow 324 | if (options.width - (options.padding.left + options.padding.right) <= 0) { 325 | options.padding.left = 0; 326 | options.padding.right = 0; 327 | } 328 | 329 | if (options.height && options.height - (options.padding.top + options.padding.bottom) <= 0) { 330 | options.padding.top = 0; 331 | options.padding.bottom = 0; 332 | } 333 | 334 | return options; 335 | }; 336 | 337 | const isHex = color => color.match(/^#(?:[0-f]{3}){1,2}$/i); 338 | const isColorValid = color => typeof color === 'string' && (chalk[color] ?? isHex(color)); 339 | const getColorFunction = color => isHex(color) ? chalk.hex(color) : chalk[color]; 340 | const getBGColorFunction = color => isHex(color) ? chalk.bgHex(color) : chalk[camelCase(['bg', color])]; 341 | 342 | export default function boxen(text, options) { 343 | options = { 344 | padding: 0, 345 | borderStyle: 'single', 346 | dimBorder: false, 347 | textAlignment: 'left', 348 | float: 'left', 349 | titleAlignment: 'left', 350 | ...options, 351 | }; 352 | 353 | // This option is deprecated 354 | if (options.align) { 355 | options.textAlignment = options.align; 356 | } 357 | 358 | if (options.borderColor && !isColorValid(options.borderColor)) { 359 | throw new Error(`${options.borderColor} is not a valid borderColor`); 360 | } 361 | 362 | if (options.backgroundColor && !isColorValid(options.backgroundColor)) { 363 | throw new Error(`${options.backgroundColor} is not a valid backgroundColor`); 364 | } 365 | 366 | options.padding = getObject(options.padding); 367 | options.margin = getObject(options.margin); 368 | 369 | options = determineDimensions(text, options); 370 | 371 | text = makeContentText(text, options); 372 | 373 | return boxContent(text, options.width, options); 374 | } 375 | 376 | export {default as _borderStyles} from 'cli-boxes'; 377 | -------------------------------------------------------------------------------- /index.test-d.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import boxen, {type Spacing, type CustomBorderStyle} from './index.js'; 3 | 4 | const border: CustomBorderStyle = { 5 | topLeft: ' ', 6 | topRight: ' ', 7 | bottomLeft: ' ', 8 | bottomRight: ' ', 9 | top: ' ', 10 | bottom: ' ', 11 | left: ' ', 12 | right: ' ', 13 | }; 14 | 15 | const spacing: Spacing = { 16 | top: 1, 17 | right: 0, 18 | bottom: 1, 19 | left: 0, 20 | }; 21 | 22 | expectType(boxen('unicorns')); 23 | expectType(boxen('unicorns', {title: 'title'})); 24 | expectType(boxen('unicorns', {title: 'title', titleAlignment: 'center'})); 25 | expectType(boxen('unicorns', {borderColor: 'green'})); 26 | expectType(boxen('unicorns', {borderColor: '#ff0000'})); 27 | expectType(boxen('unicorns', {borderStyle: 'double'})); 28 | expectType(boxen('unicorns', {borderStyle: border})); 29 | expectType(boxen('unicorns', {dimBorder: true})); 30 | expectType(boxen('unicorns', {padding: 3})); 31 | expectType(boxen('unicorns', {padding: spacing})); 32 | expectType(boxen('unicorns', {margin: 3})); 33 | expectType(boxen('unicorns', {margin: spacing})); 34 | expectType(boxen('unicorns', {float: 'center'})); 35 | expectType(boxen('unicorns', {backgroundColor: 'green'})); 36 | expectType(boxen('unicorns', {backgroundColor: '#ff0000'})); 37 | expectType(boxen('unicorns', {textAlignment: 'right'})); 38 | expectType(boxen('unicorns', {width: 20})); 39 | expectType(boxen('unicorns', {height: 5})); 40 | expectType(boxen('unicorns', {fullscreen: true})); 41 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (https://sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "boxen", 3 | "version": "8.0.1", 4 | "description": "Create boxes in the terminal", 5 | "license": "MIT", 6 | "repository": "sindresorhus/boxen", 7 | "funding": "https://github.com/sponsors/sindresorhus", 8 | "author": { 9 | "name": "Sindre Sorhus", 10 | "email": "sindresorhus@gmail.com", 11 | "url": "https://sindresorhus.com" 12 | }, 13 | "type": "module", 14 | "exports": { 15 | "types": "./index.d.ts", 16 | "default": "./index.js" 17 | }, 18 | "sideEffects": false, 19 | "engines": { 20 | "node": ">=18" 21 | }, 22 | "scripts": { 23 | "test": "xo && nyc ava && tsd" 24 | }, 25 | "files": [ 26 | "index.js", 27 | "index.d.ts" 28 | ], 29 | "keywords": [ 30 | "cli", 31 | "box", 32 | "boxes", 33 | "terminal", 34 | "term", 35 | "console", 36 | "ascii", 37 | "unicode", 38 | "border", 39 | "text" 40 | ], 41 | "dependencies": { 42 | "ansi-align": "^3.0.1", 43 | "camelcase": "^8.0.0", 44 | "chalk": "^5.3.0", 45 | "cli-boxes": "^3.0.0", 46 | "string-width": "^7.2.0", 47 | "type-fest": "^4.21.0", 48 | "widest-line": "^5.0.0", 49 | "wrap-ansi": "^9.0.0" 50 | }, 51 | "devDependencies": { 52 | "ava": "^6.1.3", 53 | "nyc": "^17.0.0", 54 | "tsd": "^0.31.1", 55 | "xo": "^0.58.0" 56 | }, 57 | "ava": { 58 | "snapshotDir": "tests/snapshots", 59 | "environmentVariables": { 60 | "COLUMNS": "60", 61 | "FORCE_COLOR": "0" 62 | } 63 | }, 64 | "xo": { 65 | "rules": { 66 | "@typescript-eslint/no-unsafe-assignment": "off" 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # boxen 2 | 3 | > Create boxes in the terminal 4 | 5 | ![](screenshot.png) 6 | 7 | ## Install 8 | 9 | ```sh 10 | npm install boxen 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```js 16 | import boxen from 'boxen'; 17 | 18 | console.log(boxen('unicorn', {padding: 1})); 19 | /* 20 | ┌─────────────┐ 21 | │ │ 22 | │ unicorn │ 23 | │ │ 24 | └─────────────┘ 25 | */ 26 | 27 | console.log(boxen('unicorn', {padding: 1, margin: 1, borderStyle: 'double'})); 28 | /* 29 | 30 | ╔═════════════╗ 31 | ║ ║ 32 | ║ unicorn ║ 33 | ║ ║ 34 | ╚═════════════╝ 35 | 36 | */ 37 | 38 | console.log(boxen('unicorns love rainbows', {title: 'magical', titleAlignment: 'center'})); 39 | /* 40 | ┌────── magical ───────┐ 41 | │unicorns love rainbows│ 42 | └──────────────────────┘ 43 | */ 44 | ``` 45 | 46 | ## API 47 | 48 | ### boxen(text, options?) 49 | 50 | #### text 51 | 52 | Type: `string` 53 | 54 | Text inside the box. 55 | 56 | #### options 57 | 58 | Type: `object` 59 | 60 | ##### borderColor 61 | 62 | Type: `string`\ 63 | Values: `'black'` `'red'` `'green'` `'yellow'` `'blue'` `'magenta'` `'cyan'` `'white'` `'gray'` or a hex value like `'#ff0000'` 64 | 65 | Color of the box border. 66 | 67 | ##### borderStyle 68 | 69 | Type: `string | object`\ 70 | Default: `'single'`\ 71 | Values: 72 | - `'single'` 73 | ``` 74 | ┌───┐ 75 | │foo│ 76 | └───┘ 77 | ``` 78 | - `'double'` 79 | ``` 80 | ╔═══╗ 81 | ║foo║ 82 | ╚═══╝ 83 | ``` 84 | - `'round'` (`'single'` sides with round corners) 85 | ``` 86 | ╭───╮ 87 | │foo│ 88 | ╰───╯ 89 | ``` 90 | - `'bold'` 91 | ``` 92 | ┏━━━┓ 93 | ┃foo┃ 94 | ┗━━━┛ 95 | ``` 96 | - `'singleDouble'` (`'single'` on top and bottom, `'double'` on right and left) 97 | ``` 98 | ╓───╖ 99 | ║foo║ 100 | ╙───╜ 101 | ``` 102 | - `'doubleSingle'` (`'double'` on top and bottom, `'single'` on right and left) 103 | ``` 104 | ╒═══╕ 105 | │foo│ 106 | ╘═══╛ 107 | ``` 108 | - `'classic'` 109 | ``` 110 | +---+ 111 | |foo| 112 | +---+ 113 | ``` 114 | - `'arrow'` 115 | ``` 116 | ↘↓↓↓↙ 117 | →foo← 118 | ↗↑↑↑↖ 119 | ``` 120 | - `'none'` 121 | ``` 122 | foo 123 | ``` 124 | 125 | Style of the box border. 126 | 127 | Can be any of the above predefined styles or an object with the following keys: 128 | 129 | ```js 130 | { 131 | topLeft: '+', 132 | topRight: '+', 133 | bottomLeft: '+', 134 | bottomRight: '+', 135 | top: '-', 136 | bottom: '-', 137 | left: '|', 138 | right: '|' 139 | } 140 | ``` 141 | 142 | ##### dimBorder 143 | 144 | Type: `boolean`\ 145 | Default: `false` 146 | 147 | Reduce opacity of the border. 148 | 149 | ##### title 150 | 151 | Type: `string` 152 | 153 | Display a title at the top of the box. 154 | If needed, the box will horizontally expand to fit the title. 155 | 156 | Example: 157 | ```js 158 | console.log(boxen('foo bar', {title: 'example'})); 159 | /* 160 | ┌ example ┐ 161 | │foo bar │ 162 | └─────────┘ 163 | */ 164 | ``` 165 | 166 | ##### titleAlignment 167 | 168 | Type: `string`\ 169 | Default: `'left'` 170 | 171 | Align the title in the top bar. 172 | 173 | Values: 174 | - `'left'` 175 | ```js 176 | /* 177 | ┌ example ──────┐ 178 | │foo bar foo bar│ 179 | └───────────────┘ 180 | */ 181 | ``` 182 | - `'center'` 183 | ```js 184 | /* 185 | ┌─── example ───┐ 186 | │foo bar foo bar│ 187 | └───────────────┘ 188 | */ 189 | ``` 190 | - `'right'` 191 | ```js 192 | /* 193 | ┌────── example ┐ 194 | │foo bar foo bar│ 195 | └───────────────┘ 196 | */ 197 | ``` 198 | 199 | ##### width 200 | 201 | Type: `number` 202 | 203 | Set a fixed width for the box. 204 | 205 | *Note:* This disables terminal overflow handling and may cause the box to look broken if the user's terminal is not wide enough. 206 | 207 | ```js 208 | import boxen from 'boxen'; 209 | 210 | console.log(boxen('foo bar', {width: 15})); 211 | // ┌─────────────┐ 212 | // │foo bar │ 213 | // └─────────────┘ 214 | ``` 215 | 216 | ##### height 217 | 218 | Type: `number` 219 | 220 | Set a fixed height for the box. 221 | 222 | *Note:* This option will crop overflowing content. 223 | 224 | ```js 225 | import boxen from 'boxen'; 226 | 227 | console.log(boxen('foo bar', {height: 5})); 228 | // ┌───────┐ 229 | // │foo bar│ 230 | // │ │ 231 | // │ │ 232 | // └───────┘ 233 | ``` 234 | 235 | ##### fullscreen 236 | 237 | Type: `boolean | (width: number, height: number) => [width: number, height: number]` 238 | 239 | Whether or not to fit all available space within the terminal. 240 | 241 | Pass a callback function to control box dimensions: 242 | 243 | ```js 244 | import boxen from 'boxen'; 245 | 246 | console.log(boxen('foo bar', { 247 | fullscreen: (width, height) => [width, height - 1], 248 | })); 249 | ``` 250 | 251 | ##### padding 252 | 253 | Type: `number | object`\ 254 | Default: `0` 255 | 256 | Space between the text and box border. 257 | 258 | Accepts a number or an object with any of the `top`, `right`, `bottom`, `left` properties. When a number is specified, the left/right padding is 3 times the top/bottom to make it look nice. 259 | 260 | ##### margin 261 | 262 | Type: `number | object`\ 263 | Default: `0` 264 | 265 | Space around the box. 266 | 267 | Accepts a number or an object with any of the `top`, `right`, `bottom`, `left` properties. When a number is specified, the left/right margin is 3 times the top/bottom to make it look nice. 268 | 269 | ##### float 270 | 271 | Type: `string`\ 272 | Default: `'left'`\ 273 | Values: `'right'` `'center'` `'left'` 274 | 275 | Float the box on the available terminal screen space. 276 | 277 | ##### backgroundColor 278 | 279 | Type: `string`\ 280 | Values: `'black'` `'red'` `'green'` `'yellow'` `'blue'` `'magenta'` `'cyan'` `'white'` `'gray'` or a hex value like `'#ff0000'` 281 | 282 | Color of the background. 283 | 284 | ##### textAlignment 285 | 286 | Type: `string`\ 287 | Default: `'left'`\ 288 | Values: `'left'` `'center'` `'right'` 289 | 290 | Align the text in the box based on the widest line. 291 | 292 | ## Maintainer 293 | 294 | - [Sindre Sorhus](https://github.com/sindresorhus) 295 | - [Caesarovich](https://github.com/Caesarovich) 296 | 297 | ## Related 298 | 299 | - [boxen-cli](https://github.com/sindresorhus/boxen-cli) - CLI for this module 300 | - [cli-boxes](https://github.com/sindresorhus/cli-boxes) - Boxes for use in the terminal 301 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/screenshot.png -------------------------------------------------------------------------------- /tests/background-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('backgroundColor option', t => { 5 | const box = boxen('foo', {backgroundColor: 'red'}); 6 | 7 | t.snapshot(box); 8 | }); 9 | 10 | test('backgroundColor hex', t => { 11 | const box = boxen('foo', {backgroundColor: '#FF0000'}); 12 | 13 | t.snapshot(box); 14 | }); 15 | 16 | test('throws on unexpected backgroundColor', t => { 17 | t.throws(() => { 18 | boxen('foo', {backgroundColor: 'dark-yellow'}); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/border-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('border color (red)', t => { 5 | const box = boxen('foo', { 6 | borderColor: 'red', 7 | }); 8 | 9 | t.snapshot(box); 10 | }); 11 | 12 | test('border color (blue)', t => { 13 | const box = boxen('foo', { 14 | borderColor: 'blue', 15 | }); 16 | 17 | t.snapshot(box); 18 | }); 19 | 20 | test('border color (green)', t => { 21 | const box = boxen('foo', { 22 | borderColor: 'green', 23 | }); 24 | 25 | t.snapshot(box); 26 | }); 27 | 28 | test('border color (yellow + dim)', t => { 29 | const box = boxen('foo', { 30 | borderColor: 'green', 31 | dimBorder: true, 32 | }); 33 | 34 | t.snapshot(box); 35 | }); 36 | 37 | test('border color (hex)', t => { 38 | const box = boxen('foo', { 39 | borderColor: '#FF00FF', 40 | dimBorder: true, 41 | }); 42 | 43 | t.snapshot(box); 44 | }); 45 | 46 | test('throws on unexpected borderColor', t => { 47 | t.throws(() => { 48 | boxen('foo', {borderColor: 'greasy-white'}); 49 | }); 50 | }); 51 | 52 | test('border style (single)', t => { 53 | const box = boxen('foo', { 54 | borderStyle: 'single', 55 | }); 56 | 57 | t.snapshot(box); 58 | }); 59 | 60 | test('border style (singleDouble)', t => { 61 | const box = boxen('foo', { 62 | borderStyle: 'singleDouble', 63 | }); 64 | 65 | t.snapshot(box); 66 | }); 67 | 68 | test('border style (doubleSingle)', t => { 69 | const box = boxen('foo', { 70 | borderStyle: 'doubleSingle', 71 | }); 72 | 73 | t.snapshot(box); 74 | }); 75 | 76 | test('border style (double)', t => { 77 | const box = boxen('foo', { 78 | borderStyle: 'double', 79 | }); 80 | 81 | t.snapshot(box); 82 | }); 83 | 84 | test('border style (classic)', t => { 85 | const box = boxen('foo', { 86 | borderStyle: 'classic', 87 | }); 88 | 89 | t.snapshot(box); 90 | }); 91 | 92 | test('border style (bold)', t => { 93 | const box = boxen('foo', { 94 | borderStyle: 'bold', 95 | }); 96 | 97 | t.snapshot(box); 98 | }); 99 | 100 | test('border style (round)', t => { 101 | const box = boxen('foo', { 102 | borderStyle: 'round', 103 | }); 104 | 105 | t.snapshot(box); 106 | }); 107 | 108 | test('border style (none)', t => { 109 | const box = boxen('foo', { 110 | borderStyle: 'none', 111 | }); 112 | 113 | t.snapshot(box); 114 | }); 115 | 116 | test('border style (custom ascii style)', t => { 117 | const box = boxen('foo', { 118 | borderStyle: { 119 | topLeft: '1', 120 | topRight: '2', 121 | bottomLeft: '3', 122 | bottomRight: '4', 123 | left: '|', 124 | right: '!', 125 | top: '-', 126 | bottom: '_', 127 | }, 128 | }); 129 | 130 | t.snapshot(box); 131 | }); 132 | 133 | test('throws on unexpected borderStyle as string', t => { 134 | t.throws(() => { 135 | boxen('foo', {borderStyle: 'shakenSnake'}); 136 | }); 137 | }); 138 | 139 | test('throws on unexpected borderStyle as object', t => { 140 | t.throws(() => { 141 | boxen('foo', {borderStyle: {shake: 'snake'}}); 142 | }); 143 | 144 | // Missing bottomRight 145 | const invalid = { 146 | topLeft: '1', 147 | topRight: '2', 148 | bottomLeft: '3', 149 | horizontal: '-', 150 | vertical: '|', 151 | }; 152 | 153 | t.throws(() => { 154 | boxen('foo', {borderStyle: invalid}); 155 | }); 156 | }); 157 | -------------------------------------------------------------------------------- /tests/float-option.js: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import test from 'ava'; 3 | import boxen from '../index.js'; 4 | 5 | test('float option (left)', t => { 6 | const box = boxen('foo', { 7 | float: 'left', 8 | }); 9 | 10 | t.snapshot(box); 11 | }); 12 | 13 | test('float option (center)', t => { 14 | const box = boxen('foo', { 15 | float: 'center', 16 | }); 17 | 18 | t.snapshot(box); 19 | }); 20 | 21 | test('float option (right)', t => { 22 | const box = boxen('foo', { 23 | float: 'right', 24 | }); 25 | 26 | t.snapshot(box); 27 | }); 28 | 29 | test('float option (center) with margin', t => { 30 | const box = boxen('foo', { 31 | float: 'right', 32 | margin: { 33 | left: 3, 34 | top: 4, 35 | }, 36 | }); 37 | 38 | t.snapshot(box); 39 | }); 40 | 41 | test('float option (right) with margin', t => { 42 | const box = boxen('foo', { 43 | float: 'right', 44 | margin: { 45 | right: 2, 46 | bottom: 5, 47 | }, 48 | }); 49 | 50 | t.snapshot(box); 51 | }); 52 | 53 | test('float option (center) when content > columns', t => { 54 | const longContent = 'foobar'.repeat(process.env.COLUMNS); 55 | 56 | t.notThrows(() => { 57 | boxen(longContent, { 58 | float: 'center', 59 | }); 60 | }); 61 | 62 | const box = boxen(longContent, { 63 | float: 'center', 64 | }); 65 | 66 | t.snapshot(box); 67 | }); 68 | 69 | test('float option (right) when content > columns', t => { 70 | const longContent = 'foobar'.repeat(process.env.COLUMNS); 71 | 72 | t.notThrows(() => { 73 | boxen(longContent, { 74 | float: 'right', 75 | }); 76 | }); 77 | 78 | const box = boxen(longContent, { 79 | float: 'right', 80 | }); 81 | 82 | t.snapshot(box); 83 | }); 84 | -------------------------------------------------------------------------------- /tests/fullscreen-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('fullscreen option', t => { 5 | const box = boxen('foo', { 6 | fullscreen: true, 7 | }); 8 | 9 | t.snapshot(box); 10 | }); 11 | 12 | test('fullscreen option + width', t => { 13 | const box = boxen('foo', { 14 | fullscreen: true, 15 | width: 10, 16 | }); 17 | 18 | t.snapshot(box); 19 | }); 20 | 21 | test('fullscreen option + height', t => { 22 | const box = boxen('foo', { 23 | fullscreen: true, 24 | height: 10, 25 | }); 26 | 27 | t.snapshot(box); 28 | }); 29 | 30 | test('fullscreen option with callback', t => { 31 | const box = boxen('foo', { 32 | fullscreen: (width, height) => [width - 2, height - 2], 33 | }); 34 | 35 | t.snapshot(box); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/height-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('height option works', t => { 5 | // Creates a tall box with empty rows 6 | t.snapshot( 7 | boxen('foo', { 8 | height: 5, 9 | }), 10 | ); 11 | 12 | // Creates a 1 line box, cropping the other lines 13 | t.snapshot( 14 | boxen('foo bar\nfoo bar', { 15 | height: 3, 16 | }), 17 | ); 18 | }); 19 | 20 | test('height option with padding + margin', t => { 21 | // Creates a wide box for little text 22 | const box = boxen('foo', { 23 | height: 20, 24 | margin: 2, 25 | padding: 1, 26 | }); 27 | 28 | t.snapshot(box); 29 | }); 30 | 31 | test('height option with width', t => { 32 | // Creates a wide box for little text 33 | const box = boxen('foo', { 34 | height: 5, 35 | width: 20, 36 | }); 37 | 38 | t.snapshot(box); 39 | }); 40 | 41 | test('height option with width + padding + margin', t => { 42 | // Creates a wide box for little text 43 | const box = boxen('foo', { 44 | height: 5, 45 | width: 20, 46 | margin: 2, 47 | padding: 1, 48 | }); 49 | 50 | t.snapshot(box); 51 | }); 52 | 53 | test('height option with border style (none)', t => { 54 | const box = boxen('foo', { 55 | height: 3, 56 | borderStyle: 'none', 57 | }); 58 | 59 | t.snapshot(box); 60 | }); 61 | -------------------------------------------------------------------------------- /tests/main.js: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import test from 'ava'; 3 | import chalk from 'chalk'; 4 | import boxen from '../index.js'; 5 | 6 | const longText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas id erat arcu. Integer urna mauris, sodales vel egestas eu, consequat id turpis. Vivamus faucibus est mattis tincidunt lobortis. In aliquam placerat nunc eget viverra. Duis aliquet faucibus diam, blandit tincidunt magna congue eu. Sed vel ante vestibulum, maximus risus eget, iaculis velit. Quisque id dapibus purus, ut sodales lorem. Aenean laoreet iaculis tellus at malesuada. Donec imperdiet eu lacus vitae fringilla.'; 7 | 8 | const formattedText = ` 9 | !!! Unicorns are lit !!! 10 | Hello this is a formatted text ! 11 | It has alignements 12 | already includes${' '} 13 | in it.${' '} 14 | Boxen should protect this alignement, 15 | otherwise the users would be sad ! 16 | Hehe Haha${' '.repeat(33)} 17 | Hihi Hoho 18 | All this garbage is on purpose. 19 | Have a good day ! 20 | `; 21 | 22 | const randomText = 'lewb{+^PN_6-l 8eK2eqB:jn^YFgGl;wuT)mdA9TZlf 9}?X#P49`x"@+nLx:BH5p{5_b`S\'E8\0{A0l"(62`TIf(z8n2arEY~]y|bk,6,FYf~rGY*Xfa00q{=fdm=4.zVf6#\'|3S!`pJ3 6y02]nj2o4?-`1v$mudH?Wbw3fZ]a+aE\'\'P4Q(6:NHBry)L_&/7v]0\0~y8PZ*|-BRY&m%UaCe\'3A,N?8&wbOP}*.O<47rnPzxO=4"*|[%A):;E)Z6!V&x!1*OprW-*+qUp&=P6M_VGx0O/VOvPEez:7C58a^.N,"Rxc|a6C[i$3QC_)~x!wd+ZMtYsGF&?'; 23 | 24 | test('creates a box', t => { 25 | const box = boxen('foo'); 26 | 27 | t.snapshot(box); 28 | }); 29 | 30 | test('box not overflowing terminal', t => { 31 | const box = boxen('foo'.repeat(process.env.COLUMNS)); 32 | 33 | t.snapshot(box); 34 | }); 35 | 36 | test('box not overflowing terminal with padding', t => { 37 | const box = boxen('foo'.repeat(process.env.COLUMNS), { 38 | padding: 3, 39 | }); 40 | 41 | t.snapshot(box); 42 | }); 43 | 44 | test('box not overflowing terminal with words', t => { 45 | const box = boxen('foo '.repeat(process.env.COLUMNS)); 46 | 47 | t.snapshot(box); 48 | }); 49 | 50 | test('box not overflowing terminal with words + padding', t => { 51 | const box = boxen('foo '.repeat(process.env.COLUMNS), { 52 | padding: 2, 53 | }); 54 | 55 | t.snapshot(box); 56 | }); 57 | 58 | test('box not overflowing terminal with words + padding + margin', t => { 59 | const box = boxen('foo '.repeat(process.env.COLUMNS), { 60 | padding: 2, 61 | magin: 1, 62 | }); 63 | 64 | t.snapshot(box); 65 | }); 66 | 67 | test('handles long text', t => { 68 | const box = boxen(longText); 69 | 70 | t.snapshot(box); 71 | }); 72 | 73 | test('handles formatted text', t => { 74 | const box = boxen(formattedText); 75 | 76 | t.snapshot(box); 77 | }); 78 | 79 | test('handles random text', t => { 80 | const box = boxen(randomText); 81 | 82 | t.snapshot(box); 83 | }); 84 | 85 | test('handles colored texts', t => { 86 | let box = boxen(chalk.red(longText)); 87 | 88 | t.snapshot(box); 89 | 90 | box = boxen(chalk.blue(formattedText)); 91 | 92 | t.snapshot(box); 93 | 94 | box = boxen(chalk.yellow(randomText)); 95 | 96 | t.snapshot(box); 97 | }); 98 | -------------------------------------------------------------------------------- /tests/margin-option.js: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import test from 'ava'; 3 | import boxen from '../index.js'; 4 | 5 | test('margin option works', t => { 6 | const box = boxen('foo', { 7 | margin: 2, 8 | }); 9 | 10 | t.snapshot(box); 11 | }); 12 | 13 | test('margin option with custom margins', t => { 14 | const box = boxen('foo', { 15 | margin: { 16 | top: 1, 17 | left: 2, 18 | right: 3, 19 | bottom: 4, 20 | }, 21 | }); 22 | 23 | t.snapshot(box); 24 | }); 25 | 26 | test('margin option with padding', t => { 27 | const box = boxen('foo', { 28 | margin: 1, 29 | padding: 1, 30 | }); 31 | 32 | t.snapshot(box); 33 | }); 34 | 35 | test('margin proportionally decreases when content <= columns', t => { 36 | // Plenty space 37 | let box = boxen('x'.repeat((process.env.COLUMNS / 2) - 2), { 38 | margin: 2, 39 | }); 40 | 41 | t.snapshot(box); 42 | 43 | // A bit of space 44 | box = boxen('x'.repeat(process.env.COLUMNS - 6 - 2), { 45 | margin: 2, 46 | }); 47 | 48 | t.snapshot(box); 49 | 50 | // No room 51 | box = boxen('ax'.repeat(process.env.COLUMNS - 2), { 52 | margin: 2, 53 | }); 54 | 55 | t.snapshot(box); 56 | }); 57 | 58 | test('margin option with border style (none)', t => { 59 | const box = boxen('foo', { 60 | margin: { 61 | top: 1, 62 | bottom: 1, 63 | left: 1, 64 | right: 1, 65 | }, 66 | borderStyle: 'none', 67 | }); 68 | 69 | t.snapshot(box); 70 | }); 71 | -------------------------------------------------------------------------------- /tests/padding-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('padding option works', t => { 5 | const box = boxen('foo', { 6 | padding: 2, 7 | }); 8 | 9 | t.snapshot(box); 10 | }); 11 | 12 | test('padding option advanced', t => { 13 | const box = boxen('foo', { 14 | padding: { 15 | top: 0, 16 | bottom: 2, 17 | left: 5, 18 | right: 10, 19 | }, 20 | }); 21 | 22 | t.snapshot(box); 23 | }); 24 | 25 | test('padding option with border style (none)', t => { 26 | const box = boxen('foo', { 27 | padding: { 28 | top: 1, 29 | bottom: 1, 30 | left: 1, 31 | right: 1, 32 | }, 33 | borderStyle: 'none', 34 | }); 35 | 36 | t.snapshot(box); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/snapshots/tests/background-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/background-option.js` 2 | 3 | The actual snapshot is saved in `background-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## backgroundColor option 8 | 9 | > Snapshot 1 10 | 11 | `┌───┐␊ 12 | │foo│␊ 13 | └───┘` 14 | 15 | ## backgroundColor hex 16 | 17 | > Snapshot 1 18 | 19 | `┌───┐␊ 20 | │foo│␊ 21 | └───┘` 22 | -------------------------------------------------------------------------------- /tests/snapshots/tests/background-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/background-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/border-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/border-option.js` 2 | 3 | The actual snapshot is saved in `border-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## border color (red) 8 | 9 | > Snapshot 1 10 | 11 | `┌───┐␊ 12 | │foo│␊ 13 | └───┘` 14 | 15 | ## border color (blue) 16 | 17 | > Snapshot 1 18 | 19 | `┌───┐␊ 20 | │foo│␊ 21 | └───┘` 22 | 23 | ## border color (green) 24 | 25 | > Snapshot 1 26 | 27 | `┌───┐␊ 28 | │foo│␊ 29 | └───┘` 30 | 31 | ## border color (yellow + dim) 32 | 33 | > Snapshot 1 34 | 35 | `┌───┐␊ 36 | │foo│␊ 37 | └───┘` 38 | 39 | ## border color (hex) 40 | 41 | > Snapshot 1 42 | 43 | `┌───┐␊ 44 | │foo│␊ 45 | └───┘` 46 | 47 | ## border style (single) 48 | 49 | > Snapshot 1 50 | 51 | `┌───┐␊ 52 | │foo│␊ 53 | └───┘` 54 | 55 | ## border style (singleDouble) 56 | 57 | > Snapshot 1 58 | 59 | `╓───╖␊ 60 | ║foo║␊ 61 | ╙───╜` 62 | 63 | ## border style (doubleSingle) 64 | 65 | > Snapshot 1 66 | 67 | `╒═══╕␊ 68 | │foo│␊ 69 | ╘═══╛` 70 | 71 | ## border style (double) 72 | 73 | > Snapshot 1 74 | 75 | `╔═══╗␊ 76 | ║foo║␊ 77 | ╚═══╝` 78 | 79 | ## border style (classic) 80 | 81 | > Snapshot 1 82 | 83 | `+---+␊ 84 | |foo|␊ 85 | +---+` 86 | 87 | ## border style (bold) 88 | 89 | > Snapshot 1 90 | 91 | `┏━━━┓␊ 92 | ┃foo┃␊ 93 | ┗━━━┛` 94 | 95 | ## border style (round) 96 | 97 | > Snapshot 1 98 | 99 | `╭───╮␊ 100 | │foo│␊ 101 | ╰───╯` 102 | 103 | ## border style (none) 104 | 105 | > Snapshot 1 106 | 107 | 'foo' 108 | 109 | ## border style (custom ascii style) 110 | 111 | > Snapshot 1 112 | 113 | `1---2␊ 114 | |foo!␊ 115 | 3___4` 116 | -------------------------------------------------------------------------------- /tests/snapshots/tests/border-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/border-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/float-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/float-option.js` 2 | 3 | The actual snapshot is saved in `float-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## float option (left) 8 | 9 | > Snapshot 1 10 | 11 | `┌───┐␊ 12 | │foo│␊ 13 | └───┘` 14 | 15 | ## float option (center) 16 | 17 | > Snapshot 1 18 | 19 | ` ┌───┐␊ 20 | │foo│␊ 21 | └───┘` 22 | 23 | ## float option (right) 24 | 25 | > Snapshot 1 26 | 27 | ` ┌───┐␊ 28 | │foo│␊ 29 | └───┘` 30 | 31 | ## float option (center) with margin 32 | 33 | > Snapshot 1 34 | 35 | `␊ 36 | ␊ 37 | ␊ 38 | ␊ 39 | ┌───┐␊ 40 | │foo│␊ 41 | └───┘` 42 | 43 | ## float option (right) with margin 44 | 45 | > Snapshot 1 46 | 47 | ` ┌───┐␊ 48 | │foo│␊ 49 | └───┘␊ 50 | ␊ 51 | ␊ 52 | ␊ 53 | ␊ 54 | ` 55 | 56 | ## float option (center) when content > columns 57 | 58 | > Snapshot 1 59 | 60 | `┌──────────────────────────────────────────────────────────┐␊ 61 | │foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoob│␊ 62 | │arfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfo│␊ 63 | │obarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar│␊ 64 | │foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoob│␊ 65 | │arfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfo│␊ 66 | │obarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar│␊ 67 | │foobarfoobar │␊ 68 | └──────────────────────────────────────────────────────────┘` 69 | 70 | ## float option (right) when content > columns 71 | 72 | > Snapshot 1 73 | 74 | `┌──────────────────────────────────────────────────────────┐␊ 75 | │foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoob│␊ 76 | │arfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfo│␊ 77 | │obarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar│␊ 78 | │foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoob│␊ 79 | │arfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfo│␊ 80 | │obarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar│␊ 81 | │foobarfoobar │␊ 82 | └──────────────────────────────────────────────────────────┘` 83 | -------------------------------------------------------------------------------- /tests/snapshots/tests/float-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/float-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/fullscreen-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/fullscreen-option.js` 2 | 3 | The actual snapshot is saved in `fullscreen-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## fullscreen option 8 | 9 | > Snapshot 1 10 | 11 | `┌───┐␊ 12 | │foo│␊ 13 | └───┘` 14 | 15 | ## fullscreen option + width 16 | 17 | > Snapshot 1 18 | 19 | `┌────────┐␊ 20 | │foo │␊ 21 | └────────┘` 22 | 23 | ## fullscreen option + height 24 | 25 | > Snapshot 1 26 | 27 | `┌───┐␊ 28 | │foo│␊ 29 | │ │␊ 30 | │ │␊ 31 | │ │␊ 32 | │ │␊ 33 | │ │␊ 34 | │ │␊ 35 | │ │␊ 36 | └───┘` 37 | 38 | ## fullscreen option with callback 39 | 40 | > Snapshot 1 41 | 42 | `┌───┐␊ 43 | │foo│␊ 44 | └───┘` 45 | -------------------------------------------------------------------------------- /tests/snapshots/tests/fullscreen-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/fullscreen-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/height-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/height-option.js` 2 | 3 | The actual snapshot is saved in `height-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## height option works 8 | 9 | > Snapshot 1 10 | 11 | `┌───┐␊ 12 | │foo│␊ 13 | │ │␊ 14 | │ │␊ 15 | └───┘` 16 | 17 | > Snapshot 2 18 | 19 | `┌───────┐␊ 20 | │foo bar│␊ 21 | └───────┘` 22 | 23 | ## height option with padding + margin 24 | 25 | > Snapshot 1 26 | 27 | `␊ 28 | ␊ 29 | ┌─────────┐␊ 30 | │ │␊ 31 | │ foo │␊ 32 | │ │␊ 33 | │ │␊ 34 | │ │␊ 35 | │ │␊ 36 | │ │␊ 37 | │ │␊ 38 | │ │␊ 39 | │ │␊ 40 | │ │␊ 41 | │ │␊ 42 | │ │␊ 43 | │ │␊ 44 | │ │␊ 45 | │ │␊ 46 | │ │␊ 47 | │ │␊ 48 | └─────────┘␊ 49 | ␊ 50 | ` 51 | 52 | ## height option with width 53 | 54 | > Snapshot 1 55 | 56 | `┌──────────────────┐␊ 57 | │foo │␊ 58 | │ │␊ 59 | │ │␊ 60 | └──────────────────┘` 61 | 62 | ## height option with width + padding + margin 63 | 64 | > Snapshot 1 65 | 66 | `␊ 67 | ␊ 68 | ┌──────────────────┐␊ 69 | │ │␊ 70 | │ foo │␊ 71 | │ │␊ 72 | └──────────────────┘␊ 73 | ␊ 74 | ` 75 | 76 | ## height option with border style (none) 77 | 78 | > Snapshot 1 79 | 80 | `foo␊ 81 | ␊ 82 | ` 83 | -------------------------------------------------------------------------------- /tests/snapshots/tests/height-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/height-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/main.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/main.js` 2 | 3 | The actual snapshot is saved in `main.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## creates a box 8 | 9 | > Snapshot 1 10 | 11 | `┌───┐␊ 12 | │foo│␊ 13 | └───┘` 14 | 15 | ## box not overflowing terminal 16 | 17 | > Snapshot 1 18 | 19 | `┌──────────────────────────────────────────────────────────┐␊ 20 | │foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoof│␊ 21 | │oofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofo│␊ 22 | │ofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo│␊ 23 | │foofoo │␊ 24 | └──────────────────────────────────────────────────────────┘` 25 | 26 | ## box not overflowing terminal with padding 27 | 28 | > Snapshot 1 29 | 30 | `┌──────────────────────────────────────────────────────────┐␊ 31 | │ │␊ 32 | │ │␊ 33 | │ │␊ 34 | │ foofoofoofoofoofoofoofoofoofoofoofoofoof │␊ 35 | │ oofoofoofoofoofoofoofoofoofoofoofoofoofo │␊ 36 | │ ofoofoofoofoofoofoofoofoofoofoofoofoofoo │␊ 37 | │ foofoofoofoofoofoofoofoofoofoofoofoofoof │␊ 38 | │ oofoofoofoofoofoofoo │␊ 39 | │ │␊ 40 | │ │␊ 41 | │ │␊ 42 | └──────────────────────────────────────────────────────────┘` 43 | 44 | ## box not overflowing terminal with words 45 | 46 | > Snapshot 1 47 | 48 | `┌────────────────────────────────────────────────────────┐␊ 49 | │foo foo foo foo foo foo foo foo foo foo foo foo foo foo │␊ 50 | │foo foo foo foo foo foo foo foo foo foo foo foo foo foo │␊ 51 | │foo foo foo foo foo foo foo foo foo foo foo foo foo foo │␊ 52 | │foo foo foo foo foo foo foo foo foo foo foo foo foo foo │␊ 53 | │foo foo foo foo │␊ 54 | └────────────────────────────────────────────────────────┘` 55 | 56 | ## box not overflowing terminal with words + padding 57 | 58 | > Snapshot 1 59 | 60 | `┌──────────────────────────────────────────────────────────┐␊ 61 | │ │␊ 62 | │ │␊ 63 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 64 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 65 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 66 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 67 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 68 | │ foo foo foo foo foo │␊ 69 | │ │␊ 70 | │ │␊ 71 | └──────────────────────────────────────────────────────────┘` 72 | 73 | ## box not overflowing terminal with words + padding + margin 74 | 75 | > Snapshot 1 76 | 77 | `┌──────────────────────────────────────────────────────────┐␊ 78 | │ │␊ 79 | │ │␊ 80 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 81 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 82 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 83 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 84 | │ foo foo foo foo foo foo foo foo foo foo foo │␊ 85 | │ foo foo foo foo foo │␊ 86 | │ │␊ 87 | │ │␊ 88 | └──────────────────────────────────────────────────────────┘` 89 | 90 | ## handles long text 91 | 92 | > Snapshot 1 93 | 94 | `┌──────────────────────────────────────────────────────────┐␊ 95 | │Lorem ipsum dolor sit amet, consectetur adipiscing elit. │␊ 96 | │Maecenas id erat arcu. Integer urna mauris, sodales vel │␊ 97 | │egestas eu, consequat id turpis. Vivamus faucibus est │␊ 98 | │mattis tincidunt lobortis. In aliquam placerat nunc eget │␊ 99 | │viverra. Duis aliquet faucibus diam, blandit tincidunt │␊ 100 | │magna congue eu. Sed vel ante vestibulum, maximus risus │␊ 101 | │eget, iaculis velit. Quisque id dapibus purus, ut sodales │␊ 102 | │lorem. Aenean laoreet iaculis tellus at malesuada. Donec │␊ 103 | │imperdiet eu lacus vitae fringilla. │␊ 104 | └──────────────────────────────────────────────────────────┘` 105 | 106 | ## handles formatted text 107 | 108 | > Snapshot 1 109 | 110 | `┌───────────────────────────────────────────────────┐␊ 111 | │ │␊ 112 | │!!! Unicorns are lit !!! │␊ 113 | │Hello this is a formatted text ! │␊ 114 | │ It has alignements │␊ 115 | │ already includes │␊ 116 | │ in it. │␊ 117 | │Boxen should protect this alignement, │␊ 118 | │ otherwise the users would be sad ! │␊ 119 | │Hehe Haha │␊ 120 | │Hihi Hoho │␊ 121 | │ All this garbage is on purpose. │␊ 122 | │Have a good day ! │␊ 123 | │ │␊ 124 | └───────────────────────────────────────────────────┘` 125 | 126 | ## handles random text 127 | 128 | > Snapshot 1 129 | 130 | `┌──────────────────────────────────────────────────────────┐␊ 131 | │lewb{+^PN_6-l 8eK2eqB:jn^YFgGl;wuT)mdA9TZlf │␊ 132 | │9}?X#P49\`x"@+nLx:BH5p{5_b\`S'E8{A0l"(62\`TIf(z8n2arEY~]y|bk,│␊ 133 | │6,FYf~rGY*Xfa00q{=fdm=4.zVf6#'|3S!\`pJ3 │␊ 134 | │6y02]nj2o4?-\`1v$mudH?Wbw3fZ]a+aE''P4Q(6:NHBry)L_&/7v]0~y8PZ*|-BRY&m%UaCe'3A,N?8&wbOP}*.O<47rnPzxO=4│␊ 136 | │"*|[%A):;E)Z6!V&x!1*OprW-*+qUp&=P6M_VGx0O│␊ 139 | │/VOvPEez:7C58a^.N,"Rxc|a6C[i$3QC_)~x!wd+ZMtYsGF&? │␊ 140 | └──────────────────────────────────────────────────────────┘` 141 | 142 | ## handles colored texts 143 | 144 | > Snapshot 1 145 | 146 | `┌──────────────────────────────────────────────────────────┐␊ 147 | │Lorem ipsum dolor sit amet, consectetur adipiscing elit. │␊ 148 | │Maecenas id erat arcu. Integer urna mauris, sodales vel │␊ 149 | │egestas eu, consequat id turpis. Vivamus faucibus est │␊ 150 | │mattis tincidunt lobortis. In aliquam placerat nunc eget │␊ 151 | │viverra. Duis aliquet faucibus diam, blandit tincidunt │␊ 152 | │magna congue eu. Sed vel ante vestibulum, maximus risus │␊ 153 | │eget, iaculis velit. Quisque id dapibus purus, ut sodales │␊ 154 | │lorem. Aenean laoreet iaculis tellus at malesuada. Donec │␊ 155 | │imperdiet eu lacus vitae fringilla. │␊ 156 | └──────────────────────────────────────────────────────────┘` 157 | 158 | > Snapshot 2 159 | 160 | `┌───────────────────────────────────────────────────┐␊ 161 | │ │␊ 162 | │!!! Unicorns are lit !!! │␊ 163 | │Hello this is a formatted text ! │␊ 164 | │ It has alignements │␊ 165 | │ already includes │␊ 166 | │ in it. │␊ 167 | │Boxen should protect this alignement, │␊ 168 | │ otherwise the users would be sad ! │␊ 169 | │Hehe Haha │␊ 170 | │Hihi Hoho │␊ 171 | │ All this garbage is on purpose. │␊ 172 | │Have a good day ! │␊ 173 | │ │␊ 174 | └───────────────────────────────────────────────────┘` 175 | 176 | > Snapshot 3 177 | 178 | `┌──────────────────────────────────────────────────────────┐␊ 179 | │lewb{+^PN_6-l 8eK2eqB:jn^YFgGl;wuT)mdA9TZlf │␊ 180 | │9}?X#P49\`x"@+nLx:BH5p{5_b\`S'E8{A0l"(62\`TIf(z8n2arEY~]y|bk,│␊ 181 | │6,FYf~rGY*Xfa00q{=fdm=4.zVf6#'|3S!\`pJ3 │␊ 182 | │6y02]nj2o4?-\`1v$mudH?Wbw3fZ]a+aE''P4Q(6:NHBry)L_&/7v]0~y8PZ*|-BRY&m%UaCe'3A,N?8&wbOP}*.O<47rnPzxO=4│␊ 184 | │"*|[%A):;E)Z6!V&x!1*OprW-*+qUp&=P6M_VGx0O│␊ 187 | │/VOvPEez:7C58a^.N,"Rxc|a6C[i$3QC_)~x!wd+ZMtYsGF&? │␊ 188 | └──────────────────────────────────────────────────────────┘` 189 | -------------------------------------------------------------------------------- /tests/snapshots/tests/main.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/main.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/margin-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/margin-option.js` 2 | 3 | The actual snapshot is saved in `margin-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## margin option works 8 | 9 | > Snapshot 1 10 | 11 | `␊ 12 | ␊ 13 | ┌───┐␊ 14 | │foo│␊ 15 | └───┘␊ 16 | ␊ 17 | ` 18 | 19 | ## margin option with custom margins 20 | 21 | > Snapshot 1 22 | 23 | `␊ 24 | ┌───┐␊ 25 | │foo│␊ 26 | └───┘␊ 27 | ␊ 28 | ␊ 29 | ␊ 30 | ` 31 | 32 | ## margin option with padding 33 | 34 | > Snapshot 1 35 | 36 | `␊ 37 | ┌─────────┐␊ 38 | │ │␊ 39 | │ foo │␊ 40 | │ │␊ 41 | └─────────┘␊ 42 | ` 43 | 44 | ## margin proportionally decreases when content <= columns 45 | 46 | > Snapshot 1 47 | 48 | `␊ 49 | ␊ 50 | ┌────────────────────────────┐␊ 51 | │xxxxxxxxxxxxxxxxxxxxxxxxxxxx│␊ 52 | └────────────────────────────┘␊ 53 | ␊ 54 | ` 55 | 56 | > Snapshot 2 57 | 58 | `␊ 59 | ␊ 60 | ┌────────────────────────────────────────────────────┐␊ 61 | │xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx│␊ 62 | └────────────────────────────────────────────────────┘␊ 63 | ␊ 64 | ` 65 | 66 | > Snapshot 3 67 | 68 | `␊ 69 | ␊ 70 | ┌──────────────────────────────────────────────────────────┐␊ 71 | │axaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxax│␊ 72 | │axaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxax│␊ 73 | └──────────────────────────────────────────────────────────┘␊ 74 | ␊ 75 | ` 76 | 77 | ## margin option with border style (none) 78 | 79 | > Snapshot 1 80 | 81 | `␊ 82 | foo␊ 83 | ` 84 | -------------------------------------------------------------------------------- /tests/snapshots/tests/margin-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/margin-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/padding-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/padding-option.js` 2 | 3 | The actual snapshot is saved in `padding-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## padding option works 8 | 9 | > Snapshot 1 10 | 11 | `┌───────────────┐␊ 12 | │ │␊ 13 | │ │␊ 14 | │ foo │␊ 15 | │ │␊ 16 | │ │␊ 17 | └───────────────┘` 18 | 19 | ## padding option advanced 20 | 21 | > Snapshot 1 22 | 23 | `┌──────────────────┐␊ 24 | │ foo │␊ 25 | │ │␊ 26 | │ │␊ 27 | └──────────────────┘` 28 | 29 | ## padding option with border style (none) 30 | 31 | > Snapshot 1 32 | 33 | ` ␊ 34 | foo ␊ 35 | ` 36 | -------------------------------------------------------------------------------- /tests/snapshots/tests/padding-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/padding-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/text-align-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/text-align-option.js` 2 | 3 | The actual snapshot is saved in `text-align-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## text alignement option (left) 8 | 9 | > Snapshot 1 10 | 11 | `┌────────────────┐␊ 12 | │Hello there ! │␊ 13 | │General Kenobi !│␊ 14 | └────────────────┘` 15 | 16 | ## text alignement option (center) 17 | 18 | > Snapshot 1 19 | 20 | `┌────────────────┐␊ 21 | │ Hello there ! │␊ 22 | │General Kenobi !│␊ 23 | └────────────────┘` 24 | 25 | ## text alignement option (right) 26 | 27 | > Snapshot 1 28 | 29 | `┌────────────────┐␊ 30 | │ Hello there !│␊ 31 | │General Kenobi !│␊ 32 | └────────────────┘` 33 | 34 | ## text alignement option (left) + padding 35 | 36 | > Snapshot 1 37 | 38 | `┌──────────────────────┐␊ 39 | │ │␊ 40 | │ Hello there ! │␊ 41 | │ General Kenobi ! │␊ 42 | │ │␊ 43 | └──────────────────────┘` 44 | 45 | ## text alignement option (center) + padding 46 | 47 | > Snapshot 1 48 | 49 | `┌──────────────────────┐␊ 50 | │ │␊ 51 | │ Hello there ! │␊ 52 | │ General Kenobi ! │␊ 53 | │ │␊ 54 | └──────────────────────┘` 55 | 56 | ## text alignement option (right) + padding 57 | 58 | > Snapshot 1 59 | 60 | `┌──────────────────────┐␊ 61 | │ │␊ 62 | │ Hello there ! │␊ 63 | │ General Kenobi ! │␊ 64 | │ │␊ 65 | └──────────────────────┘` 66 | 67 | ## text alignement option (left) + long title 68 | 69 | > Snapshot 1 70 | 71 | `┌ This is a famous movie quote: ┐␊ 72 | │Hello there ! │␊ 73 | │General Kenobi ! │␊ 74 | └───────────────────────────────┘` 75 | 76 | ## text alignement option (center) + long title 77 | 78 | > Snapshot 1 79 | 80 | `┌ This is a famous movie quote: ┐␊ 81 | │ Hello there ! │␊ 82 | │ General Kenobi ! │␊ 83 | └───────────────────────────────┘` 84 | 85 | ## text alignement option (right) + long title 86 | 87 | > Snapshot 1 88 | 89 | `┌ This is a famous movie quote: ┐␊ 90 | │ Hello there !│␊ 91 | │ General Kenobi !│␊ 92 | └───────────────────────────────┘` 93 | 94 | ## text alignement option (left) + long title + padding 95 | 96 | > Snapshot 1 97 | 98 | `┌ This is a famous movie quote: ┐␊ 99 | │ │␊ 100 | │ Hello there ! │␊ 101 | │ General Kenobi ! │␊ 102 | │ │␊ 103 | └───────────────────────────────┘` 104 | 105 | ## text alignement option (center) + long title + padding 106 | 107 | > Snapshot 1 108 | 109 | `┌ This is a famous movie quote: ┐␊ 110 | │ │␊ 111 | │ Hello there ! │␊ 112 | │ General Kenobi ! │␊ 113 | │ │␊ 114 | └───────────────────────────────┘` 115 | 116 | ## text alignement option (right) + long title + padding 117 | 118 | > Snapshot 1 119 | 120 | `┌ This is a famous movie quote: ┐␊ 121 | │ │␊ 122 | │ Hello there ! │␊ 123 | │ General Kenobi ! │␊ 124 | │ │␊ 125 | └───────────────────────────────┘` 126 | 127 | ## text alignement option (left) + long title + padding + margin 128 | 129 | > Snapshot 1 130 | 131 | `␊ 132 | ┌ This is a famous movie quote: ┐␊ 133 | │ │␊ 134 | │ Hello there ! │␊ 135 | │ General Kenobi ! │␊ 136 | │ │␊ 137 | └───────────────────────────────┘␊ 138 | ` 139 | 140 | ## text alignement option (center) + long title + padding + margin 141 | 142 | > Snapshot 1 143 | 144 | `␊ 145 | ┌ This is a famous movie quote: ┐␊ 146 | │ │␊ 147 | │ Hello there ! │␊ 148 | │ General Kenobi ! │␊ 149 | │ │␊ 150 | └───────────────────────────────┘␊ 151 | ` 152 | 153 | ## text alignement option (right) + long title + padding + margin 154 | 155 | > Snapshot 1 156 | 157 | `␊ 158 | ┌ This is a famous movie quote: ┐␊ 159 | │ │␊ 160 | │ Hello there ! │␊ 161 | │ General Kenobi ! │␊ 162 | │ │␊ 163 | └───────────────────────────────┘␊ 164 | ` 165 | 166 | ## text alignement option (center) after wrapping 167 | 168 | > Snapshot 1 169 | 170 | `┌──────────────────────────────────────────────────────────┐␊ 171 | │Lorem ipsum dolor sit amet, consectetur adipiscing elit. │␊ 172 | │ Maecenas id erat arcu. Integer urna mauris, sodales vel │␊ 173 | │ egestas eu, consequat id turpis. Vivamus faucibus est │␊ 174 | │mattis tincidunt lobortis. In aliquam placerat nunc eget │␊ 175 | │ viverra. Duis aliquet faucibus diam, blandit tincidunt │␊ 176 | │ magna congue eu. Sed vel ante vestibulum, maximus risus │␊ 177 | │eget, iaculis velit. Quisque id dapibus purus, ut sodales │␊ 178 | │lorem. Aenean laoreet iaculis tellus at malesuada. Donec │␊ 179 | │ imperdiet eu lacus vitae fringilla. │␊ 180 | └──────────────────────────────────────────────────────────┘` 181 | 182 | ## text alignement option (right) after wrapping 183 | 184 | > Snapshot 1 185 | 186 | `┌──────────────────────────────────────────────────────────┐␊ 187 | │ Lorem ipsum dolor sit amet, consectetur adipiscing elit.│␊ 188 | │ Maecenas id erat arcu. Integer urna mauris, sodales vel│␊ 189 | │ egestas eu, consequat id turpis. Vivamus faucibus est│␊ 190 | │ mattis tincidunt lobortis. In aliquam placerat nunc eget│␊ 191 | │ viverra. Duis aliquet faucibus diam, blandit tincidunt│␊ 192 | │ magna congue eu. Sed vel ante vestibulum, maximus risus│␊ 193 | │ eget, iaculis velit. Quisque id dapibus purus, ut sodales│␊ 194 | │ lorem. Aenean laoreet iaculis tellus at malesuada. Donec│␊ 195 | │ imperdiet eu lacus vitae fringilla.│␊ 196 | └──────────────────────────────────────────────────────────┘` 197 | 198 | ## text alignement option (center) after wrapping + padding 199 | 200 | > Snapshot 1 201 | 202 | `┌──────────────────────────────────────────────────────────┐␊ 203 | │ │␊ 204 | │ Lorem ipsum dolor sit amet, consectetur adipiscing │␊ 205 | │ elit. Maecenas id erat arcu. Integer urna mauris, │␊ 206 | │ sodales vel egestas eu, consequat id turpis. Vivamus │␊ 207 | │ faucibus est mattis tincidunt lobortis. In aliquam │␊ 208 | │ placerat nunc eget viverra. Duis aliquet faucibus │␊ 209 | │ diam, blandit tincidunt magna congue eu. Sed vel │␊ 210 | │ ante vestibulum, maximus risus eget, iaculis velit. │␊ 211 | │ Quisque id dapibus purus, ut sodales lorem. Aenean │␊ 212 | │ laoreet iaculis tellus at malesuada. Donec imperdiet │␊ 213 | │ eu lacus vitae fringilla. │␊ 214 | │ │␊ 215 | └──────────────────────────────────────────────────────────┘` 216 | 217 | ## text alignement option (right) after wrapping + padding + margin 218 | 219 | > Snapshot 1 220 | 221 | `␊ 222 | ┌──────────────────────────────────────────────────────────┐␊ 223 | │ │␊ 224 | │ Lorem ipsum dolor sit amet, consectetur adipiscing │␊ 225 | │ elit. Maecenas id erat arcu. Integer urna mauris, │␊ 226 | │ sodales vel egestas eu, consequat id turpis. Vivamus │␊ 227 | │ faucibus est mattis tincidunt lobortis. In aliquam │␊ 228 | │ placerat nunc eget viverra. Duis aliquet faucibus │␊ 229 | │ diam, blandit tincidunt magna congue eu. Sed vel │␊ 230 | │ ante vestibulum, maximus risus eget, iaculis velit. │␊ 231 | │ Quisque id dapibus purus, ut sodales lorem. Aenean │␊ 232 | │ laoreet iaculis tellus at malesuada. Donec imperdiet │␊ 233 | │ eu lacus vitae fringilla. │␊ 234 | │ │␊ 235 | └──────────────────────────────────────────────────────────┘␊ 236 | ` 237 | -------------------------------------------------------------------------------- /tests/snapshots/tests/text-align-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/text-align-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/title-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/title-option.js` 2 | 3 | The actual snapshot is saved in `title-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## title option works 8 | 9 | > Snapshot 1 10 | 11 | `┌ title ┐␊ 12 | │foo │␊ 13 | └───────┘` 14 | 15 | ## title align left 16 | 17 | > Snapshot 1 18 | 19 | `┌ title ────────┐␊ 20 | │foo bar foo bar│␊ 21 | └───────────────┘` 22 | 23 | ## title align center 24 | 25 | > Snapshot 1 26 | 27 | `┌──── title ────┐␊ 28 | │foo bar foo bar│␊ 29 | └───────────────┘` 30 | 31 | ## title align right 32 | 33 | > Snapshot 1 34 | 35 | `┌──────── title ┐␊ 36 | │foo bar foo bar│␊ 37 | └───────────────┘` 38 | 39 | ## long title expands box 40 | 41 | > Snapshot 1 42 | 43 | `┌ very long title ┐␊ 44 | │foo │␊ 45 | └─────────────────┘` 46 | 47 | ## title + width option 48 | 49 | > Snapshot 1 50 | 51 | `┌─┐␊ 52 | │f│␊ 53 | │o│␊ 54 | │o│␊ 55 | └─┘` 56 | 57 | > Snapshot 2 58 | 59 | `┌ v ┐␊ 60 | │foo│␊ 61 | └───┘` 62 | 63 | > Snapshot 3 64 | 65 | `┌ very long title ─┐␊ 66 | │foo │␊ 67 | └──────────────────┘` 68 | 69 | ## title option with border style (none) 70 | 71 | > Snapshot 1 72 | 73 | `title␊ 74 | foo ` 75 | -------------------------------------------------------------------------------- /tests/snapshots/tests/title-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/title-option.js.snap -------------------------------------------------------------------------------- /tests/snapshots/tests/width-option.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `tests/width-option.js` 2 | 3 | The actual snapshot is saved in `width-option.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## width option works 8 | 9 | > Snapshot 1 10 | 11 | `┌──────────────────┐␊ 12 | │foo │␊ 13 | └──────────────────┘` 14 | 15 | > Snapshot 2 16 | 17 | `┌────────┐␊ 18 | │foo bar │␊ 19 | │foo bar │␊ 20 | └────────┘` 21 | 22 | ## width option with padding + margin 23 | 24 | > Snapshot 1 25 | 26 | `␊ 27 | ␊ 28 | ┌──────────────────┐␊ 29 | │ │␊ 30 | │ foo │␊ 31 | │ │␊ 32 | └──────────────────┘␊ 33 | ␊ 34 | ` 35 | 36 | ## width option with big padding 37 | 38 | > Snapshot 1 39 | 40 | `┌────┐␊ 41 | │ │␊ 42 | │ │␊ 43 | │ │␊ 44 | │foo │␊ 45 | │ │␊ 46 | │ │␊ 47 | │ │␊ 48 | └────┘` 49 | 50 | ## width option with border style (none) 51 | 52 | > Snapshot 1 53 | 54 | 'foo' 55 | -------------------------------------------------------------------------------- /tests/snapshots/tests/width-option.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/boxen/52bbd6a57e92ea0dac762677d21ab5787a8abc39/tests/snapshots/tests/width-option.js.snap -------------------------------------------------------------------------------- /tests/text-align-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | const longText = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas id erat arcu. Integer urna mauris, sodales vel egestas eu, consequat id turpis. Vivamus faucibus est mattis tincidunt lobortis. In aliquam placerat nunc eget viverra. Duis aliquet faucibus diam, blandit tincidunt magna congue eu. Sed vel ante vestibulum, maximus risus eget, iaculis velit. Quisque id dapibus purus, ut sodales lorem. Aenean laoreet iaculis tellus at malesuada. Donec imperdiet eu lacus vitae fringilla.'; 5 | 6 | test('text alignement option (left)', t => { 7 | const box = boxen('Hello there !\nGeneral Kenobi !', { 8 | textAlignment: 'left', 9 | }); 10 | 11 | t.snapshot(box); 12 | }); 13 | 14 | test('text alignement option (center)', t => { 15 | const box = boxen('Hello there !\nGeneral Kenobi !', { 16 | textAlignment: 'center', 17 | }); 18 | 19 | t.snapshot(box); 20 | }); 21 | 22 | test('text alignement option (right)', t => { 23 | const box = boxen('Hello there !\nGeneral Kenobi !', { 24 | textAlignment: 'right', 25 | }); 26 | 27 | t.snapshot(box); 28 | }); 29 | 30 | test('text alignement option (left) + padding', t => { 31 | const box = boxen('Hello there !\nGeneral Kenobi !', { 32 | textAlignment: 'left', 33 | padding: 1, 34 | }); 35 | 36 | t.snapshot(box); 37 | }); 38 | 39 | test('text alignement option (center) + padding', t => { 40 | const box = boxen('Hello there !\nGeneral Kenobi !', { 41 | textAlignment: 'center', 42 | padding: 1, 43 | }); 44 | 45 | t.snapshot(box); 46 | }); 47 | 48 | test('text alignement option (right) + padding', t => { 49 | const box = boxen('Hello there !\nGeneral Kenobi !', { 50 | textAlignment: 'right', 51 | padding: 1, 52 | }); 53 | 54 | t.snapshot(box); 55 | }); 56 | 57 | test('text alignement option (left) + long title', t => { 58 | const box = boxen('Hello there !\nGeneral Kenobi !', { 59 | textAlignment: 'left', 60 | title: 'This is a famous movie quote:', 61 | }); 62 | 63 | t.snapshot(box); 64 | }); 65 | 66 | test('text alignement option (center) + long title', t => { 67 | const box = boxen('Hello there !\nGeneral Kenobi !', { 68 | textAlignment: 'center', 69 | title: 'This is a famous movie quote:', 70 | }); 71 | 72 | t.snapshot(box); 73 | }); 74 | 75 | test('text alignement option (right) + long title', t => { 76 | const box = boxen('Hello there !\nGeneral Kenobi !', { 77 | textAlignment: 'right', 78 | title: 'This is a famous movie quote:', 79 | }); 80 | 81 | t.snapshot(box); 82 | }); 83 | 84 | test('text alignement option (left) + long title + padding', t => { 85 | const box = boxen('Hello there !\nGeneral Kenobi !', { 86 | textAlignment: 'left', 87 | title: 'This is a famous movie quote:', 88 | padding: 1, 89 | }); 90 | 91 | t.snapshot(box); 92 | }); 93 | 94 | test('text alignement option (center) + long title + padding', t => { 95 | const box = boxen('Hello there !\nGeneral Kenobi !', { 96 | textAlignment: 'center', 97 | title: 'This is a famous movie quote:', 98 | padding: 1, 99 | }); 100 | 101 | t.snapshot(box); 102 | }); 103 | 104 | test('text alignement option (right) + long title + padding', t => { 105 | const box = boxen('Hello there !\nGeneral Kenobi !', { 106 | textAlignment: 'right', 107 | title: 'This is a famous movie quote:', 108 | padding: 1, 109 | }); 110 | 111 | t.snapshot(box); 112 | }); 113 | 114 | test('text alignement option (left) + long title + padding + margin', t => { 115 | const box = boxen('Hello there !\nGeneral Kenobi !', { 116 | textAlignment: 'left', 117 | title: 'This is a famous movie quote:', 118 | margin: 1, 119 | padding: 1, 120 | }); 121 | 122 | t.snapshot(box); 123 | }); 124 | 125 | test('text alignement option (center) + long title + padding + margin', t => { 126 | const box = boxen('Hello there !\nGeneral Kenobi !', { 127 | textAlignment: 'center', 128 | title: 'This is a famous movie quote:', 129 | margin: 1, 130 | padding: 1, 131 | }); 132 | 133 | t.snapshot(box); 134 | }); 135 | 136 | test('text alignement option (right) + long title + padding + margin', t => { 137 | const box = boxen('Hello there !\nGeneral Kenobi !', { 138 | textAlignment: 'right', 139 | title: 'This is a famous movie quote:', 140 | margin: 1, 141 | padding: 1, 142 | }); 143 | 144 | t.snapshot(box); 145 | }); 146 | 147 | test('text alignement option (center) after wrapping', t => { 148 | const box = boxen(longText, { 149 | textAlignment: 'center', 150 | }); 151 | 152 | t.snapshot(box); 153 | }); 154 | 155 | test('text alignement option (right) after wrapping', t => { 156 | const box = boxen(longText, { 157 | textAlignment: 'right', 158 | }); 159 | 160 | t.snapshot(box); 161 | }); 162 | 163 | test('text alignement option (center) after wrapping + padding', t => { 164 | const box = boxen(longText, { 165 | textAlignment: 'center', 166 | padding: 1, 167 | }); 168 | 169 | t.snapshot(box); 170 | }); 171 | 172 | test('text alignement option (right) after wrapping + padding + margin', t => { 173 | const box = boxen(longText, { 174 | textAlignment: 'center', 175 | margin: 1, 176 | padding: 1, 177 | }); 178 | 179 | t.snapshot(box); 180 | }); 181 | -------------------------------------------------------------------------------- /tests/title-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('title option works', t => { 5 | const box = boxen('foo', { 6 | title: 'title', 7 | }); 8 | 9 | t.snapshot(box); 10 | }); 11 | 12 | test('title align left', t => { 13 | const box = boxen('foo bar foo bar', { 14 | title: 'title', 15 | titleAlignment: 'left', 16 | }); 17 | 18 | t.snapshot(box); 19 | }); 20 | 21 | test('title align center', t => { 22 | const box = boxen('foo bar foo bar', { 23 | title: 'title', 24 | titleAlignment: 'center', 25 | }); 26 | 27 | t.snapshot(box); 28 | }); 29 | 30 | test('title align right', t => { 31 | const box = boxen('foo bar foo bar', { 32 | title: 'title', 33 | titleAlignment: 'right', 34 | }); 35 | 36 | t.snapshot(box); 37 | }); 38 | 39 | test('long title expands box', t => { 40 | const box = boxen('foo', { 41 | title: 'very long title', 42 | }); 43 | 44 | t.snapshot(box); 45 | }); 46 | 47 | test('title + width option', t => { 48 | // Not enough space, no title 49 | t.snapshot( 50 | boxen('foo', { 51 | title: 'very long title', 52 | width: 3, 53 | }), 54 | ); 55 | 56 | // Space for only one character 57 | t.snapshot( 58 | boxen('foo', { 59 | title: 'very long title', 60 | width: 5, 61 | }), 62 | ); 63 | 64 | t.snapshot( 65 | boxen('foo', { 66 | title: 'very long title', 67 | width: 20, 68 | }), 69 | ); 70 | }); 71 | 72 | test('title option with border style (none)', t => { 73 | const box = boxen('foo', { 74 | title: 'title', 75 | borderStyle: 'none', 76 | }); 77 | 78 | t.snapshot(box); 79 | }); 80 | -------------------------------------------------------------------------------- /tests/width-option.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import boxen from '../index.js'; 3 | 4 | test('width option works', t => { 5 | // Creates a wide box for little text 6 | t.snapshot( 7 | boxen('foo', { 8 | width: 20, 9 | }), 10 | ); 11 | 12 | // Creates a small box for a lot of text 13 | t.snapshot( 14 | boxen('foo bar foo bar', { 15 | width: 10, 16 | }), 17 | ); 18 | }); 19 | 20 | test('width option with padding + margin', t => { 21 | // Creates a wide box for little text 22 | const box = boxen('foo', { 23 | width: 20, 24 | margin: 2, 25 | padding: 1, 26 | }); 27 | 28 | t.snapshot(box); 29 | }); 30 | 31 | test('width option with big padding', t => { 32 | // Should disable the paddings 33 | const box = boxen('foo', { 34 | width: 6, 35 | padding: 3, 36 | }); 37 | 38 | t.snapshot(box); 39 | }); 40 | 41 | test('width option with border style (none)', t => { 42 | const box = boxen('foo', { 43 | width: 3, 44 | borderStyle: 'none', 45 | }); 46 | 47 | t.snapshot(box); 48 | }); 49 | --------------------------------------------------------------------------------