UNPKG

layoutz

Version:

Friendly, expressive print-layout DSL for JavaScript/TypeScript

1,176 lines (1,173 loc) 35.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AutoCentered: () => AutoCentered, Banner: () => Banner, Border: () => Border, BorderStyle: () => BorderStyle, Box: () => Box, Center: () => Center, Chart: () => Chart, Columns: () => Columns, DIMENSIONS: () => DIMENSIONS, Empty: () => Empty, GLYPHS: () => GLYPHS, HorizontalRule: () => HorizontalRule, InlineBar: () => InlineBar, Justified: () => Justified, KeyValue: () => KeyValue, Layout: () => Layout, LeftAligned: () => LeftAligned, LineBreak: () => LineBreak, Margin: () => Margin, OrderedList: () => OrderedList, Padded: () => Padded, RightAligned: () => RightAligned, Row: () => Row, Section: () => Section, StatusCard: () => StatusCard, Table: () => Table, Text: () => Text, Tree: () => Tree, Truncated: () => Truncated, Underline: () => Underline, UnorderedList: () => UnorderedList, VerticalRule: () => VerticalRule, Wrapped: () => Wrapped, autoCenter: () => autoCenter, banner: () => banner, box: () => box, br: () => br, center: () => center, chart: () => chart, columns: () => columns, dsl: () => dsl, empty: () => empty, getHeight: () => getHeight, getWidth: () => getWidth, hr: () => hr, inlineBar: () => inlineBar, justify: () => justify, kv: () => kv, layout: () => layout, leftAlign: () => leftAlign, margin: () => margin, margins: () => margins, ol: () => ol, pad: () => pad, rightAlign: () => rightAlign, row: () => row, section: () => section, statusCard: () => statusCard, stripAnsiCodes: () => stripAnsiCodes, table: () => table, text: () => text, tree: () => tree, truncate: () => truncate, ul: () => ul, underline: () => underline, vr: () => vr, wrap: () => wrap }); module.exports = __toCommonJS(index_exports); // layoutz.ts var DIMENSIONS = { MIN_CONTENT_PADDING: 2, BORDER_THICKNESS: 2, SIDE_PADDING: 2, PROGRESS_BAR_WIDTH: 20, TREE_INDENTATION: 4, TREE_CONNECTOR_SPACING: 3, DEFAULT_RULE_WIDTH: 50, DEFAULT_CHART_WIDTH: 40, CHART_LABEL_MAX_WIDTH: 15, CHART_LABEL_SPACING: 15, BOX_INNER_PADDING: 4, BOX_BORDER_WIDTH: 2 }; var GLYPHS = { TOP_LEFT: "\u250C", TOP_RIGHT: "\u2510", BOTTOM_LEFT: "\u2514", BOTTOM_RIGHT: "\u2518", HORIZONTAL: "\u2500", VERTICAL: "\u2502", CROSS: "\u253C", TEE_DOWN: "\u252C", TEE_UP: "\u2534", TEE_RIGHT: "\u251C", TEE_LEFT: "\u2524", BULLET: "\u2022", SPACE: " ", BAR_FILLED: "\u2588", BAR_EMPTY: "\u2500", TREE_BRANCH: "\u251C\u2500\u2500", TREE_LAST_BRANCH: "\u2514\u2500\u2500", TREE_VERTICAL: "\u2502", TREE_INDENT: " ".repeat(DIMENSIONS.TREE_INDENTATION) }; var ANSI_ESCAPE_REGEX = /\u001b\[[0-9;]*m/g; function stripAnsiCodes(text2) { return text2.replace(ANSI_ESCAPE_REGEX, ""); } function flattenToSingleLine(element) { return element.render().split("\n").join(" "); } function getWidth(element) { const rendered = element.render(); if (!rendered) return 0; const lines = rendered.split("\n"); return Math.max(...lines.map((line) => stripAnsiCodes(line).length)); } function getHeight(element) { const rendered = element.render(); if (!rendered) return 1; return rendered.split("\n").length; } var Text = class { constructor(content) { this.content = content; } render() { return this.content; } }; var LineBreak = class { render() { return "\n"; } }; var HorizontalRule = class { constructor(char = "\u2500", ruleWidth) { this.char = char; this.ruleWidth = ruleWidth; } render() { const width = this.ruleWidth ?? DIMENSIONS.DEFAULT_RULE_WIDTH; return this.char.repeat(width); } }; var BorderStyle = /* @__PURE__ */ ((BorderStyle2) => { BorderStyle2["Single"] = "single"; BorderStyle2["Double"] = "double"; BorderStyle2["Thick"] = "thick"; BorderStyle2["Round"] = "round"; return BorderStyle2; })(BorderStyle || {}); var Border = { Single: "single" /* Single */, Double: "double" /* Double */, Thick: "thick" /* Thick */, Round: "round" /* Round */ }; function getBorderChars(style) { switch (style) { case "single" /* Single */: return { topLeft: "\u250C", topRight: "\u2510", bottomLeft: "\u2514", bottomRight: "\u2518", horizontal: "\u2500", vertical: "\u2502" }; case "double" /* Double */: return { topLeft: "\u2554", topRight: "\u2557", bottomLeft: "\u255A", bottomRight: "\u255D", horizontal: "\u2550", vertical: "\u2551" }; case "thick" /* Thick */: return { topLeft: "\u250F", topRight: "\u2513", bottomLeft: "\u2517", bottomRight: "\u251B", horizontal: "\u2501", vertical: "\u2503" }; case "round" /* Round */: return { topLeft: "\u256D", topRight: "\u256E", bottomLeft: "\u2570", bottomRight: "\u256F", horizontal: "\u2500", vertical: "\u2502" }; } } var KeyValue = class { constructor(pairs) { this.pairs = pairs; } render() { if (this.pairs.length === 0) return ""; const maxKeyLength = Math.max(...this.pairs.map(([key]) => key.length)); const alignmentPosition = maxKeyLength + 2; return this.pairs.map(([key, value]) => { const keyWithColon = `${key}:`; const spacesNeeded = alignmentPosition - keyWithColon.length; const padding = " ".repeat(Math.max(1, spacesNeeded)); return `${keyWithColon}${padding}${value}`; }).join("\n"); } }; var _UnorderedList = class _UnorderedList { constructor(items, bullet = "\u2022") { this.items = items; this.bullet = bullet; } render() { return this.renderAtLevel(0); } renderAtLevel(level) { if (this.items.length === 0) return ""; const currentBullet = this.bullet === "\u2022" ? _UnorderedList.BULLET_STYLES[level % _UnorderedList.BULLET_STYLES.length] : this.bullet; const result = []; for (const item of this.items) { if (item instanceof _UnorderedList) { const nestedContent = item.renderAtLevel(level + 1); if (nestedContent) { result.push(nestedContent); } } else { const content = item.render(); const lines = content.split("\n"); const indent = " ".repeat(level); if (lines.length === 1) { result.push(`${indent}${currentBullet} ${lines[0]}`); } else { const firstLine = `${indent}${currentBullet} ${lines[0]}`; const lineIndent = indent + " ".repeat(currentBullet.length + 1); const remainingLines = lines.slice(1).map((line) => `${lineIndent}${line}`); result.push([firstLine, ...remainingLines].join("\n")); } } } return result.join("\n"); } }; _UnorderedList.BULLET_STYLES = ["\u2022", "\u25E6", "\u25AA"]; var UnorderedList = _UnorderedList; var Tree = class _Tree { constructor(label, children = []) { this.label = label; this.children = children; } render() { return this.renderAtLevel(0, true, []); } renderAtLevel(level, isLast, parentPrefixes) { if (level === 0 && this.children.length === 0) { return this.label; } let result = ""; if (level === 0) { result += this.label; } else { const prefix = this.buildPrefix(parentPrefixes, isLast); const suffix = this.children.length > 0 ? "/" : ""; result += prefix + this.label + suffix; } if (this.children.length > 0) { result += "\n"; const newParentPrefixes = [...parentPrefixes]; if (level > 0) { newParentPrefixes.push(!isLast); } this.children.forEach((child, index) => { const isChildLast = index === this.children.length - 1; const childTree = new _Tree(child.label, child.children); result += childTree.renderAtLevel( level + 1, isChildLast, newParentPrefixes ); if (!isChildLast) { result += "\n"; } }); } return result; } buildPrefix(parentPrefixes, isLast) { let prefix = ""; for (const hasMore of parentPrefixes) { if (hasMore) { prefix += "\u2502 "; } else { prefix += " "; } } if (isLast) { prefix += "\u2514\u2500\u2500 "; } else { prefix += "\u251C\u2500\u2500 "; } return prefix; } }; var OrderedList = class _OrderedList { constructor(items) { this.items = items; } render() { return this.renderAtLevel(0); } renderAtLevel(level) { if (this.items.length === 0) return ""; let itemNumber = 0; return this.items.map((item) => { if (item instanceof _OrderedList) { return item.renderAtLevel(level + 1); } else { const number = this.getNumbering(itemNumber, level); itemNumber++; const content = item.render(); const lines = content.split("\n"); const indent = " ".repeat(level); if (lines.length === 1) { return `${indent}${number}. ${lines[0]}`; } else { const firstLine = `${indent}${number}. ${lines[0]}`; const lineIndent = indent + " ".repeat(number.length + 2); const remainingLines = lines.slice(1).map((line) => `${lineIndent}${line}`); return [firstLine, ...remainingLines].join("\n"); } } }).join("\n"); } getNumbering(index, level) { switch (level % 3) { case 0: return (index + 1).toString(); case 1: return String.fromCharCode(97 + index); // a, b, c... case 2: return this.toRomanNumeral(index + 1); default: return (index + 1).toString(); } } toRomanNumeral(n) { const mappings = [ [10, "x"], [9, "ix"], [5, "v"], [4, "iv"], [1, "i"] ]; let result = ""; let num = n; for (const [value, symbol] of mappings) { while (num >= value) { result += symbol; num -= value; } } return result; } }; var Box = class _Box { constructor(elements, title = "", style = "single" /* Single */) { this.elements = elements; this.title = title; this.style = style; } /** * Set the border style (fluent API) */ border(style) { return new _Box(this.elements, this.title, style); } render() { const content = this.elements.length === 1 ? this.elements[0] : new Layout(this.elements); const contentLines = content.render().split("\n"); const contentWidth = contentLines.length === 0 ? 0 : Math.max(...contentLines.map((line) => stripAnsiCodes(line).length)); const titleWidth = this.title ? this.title.length + DIMENSIONS.MIN_CONTENT_PADDING : 0; const innerWidth = Math.max(contentWidth, titleWidth); const totalWidth = innerWidth + DIMENSIONS.BOX_INNER_PADDING; const chars = getBorderChars(this.style); const topBorder = this.title ? (() => { const titlePadding = totalWidth - this.title.length - DIMENSIONS.BOX_BORDER_WIDTH; const leftPad = Math.floor(titlePadding / 2); const rightPad = titlePadding - leftPad; return `${chars.topLeft}${chars.horizontal.repeat(leftPad)}${this.title}${chars.horizontal.repeat(rightPad)}${chars.topRight}`; })() : `${chars.topLeft}${chars.horizontal.repeat(totalWidth - DIMENSIONS.BOX_BORDER_WIDTH)}${chars.topRight}`; const bottomBorder = `${chars.bottomLeft}${chars.horizontal.repeat(totalWidth - DIMENSIONS.BOX_BORDER_WIDTH)}${chars.bottomRight}`; const paddedContent = contentLines.map((line) => { const padding = innerWidth - stripAnsiCodes(line).length; return `${chars.vertical} ${line}${" ".repeat(padding)} ${chars.vertical}`; }); return [topBorder, ...paddedContent, bottomBorder].join("\n"); } }; var Section = class { constructor(title, content, glyph = "=", flankingChars = 3) { this.title = title; this.content = content; this.glyph = glyph; this.flankingChars = flankingChars; } render() { const header = `${this.glyph.repeat(this.flankingChars)} ${this.title} ${this.glyph.repeat(this.flankingChars)}`; return `${header} ${this.content.render()}`; } }; var Row = class { constructor(elements) { this.elements = elements; } render() { if (this.elements.length === 0) return ""; const renderedElements = this.elements.map((el) => el.render().split("\n")); const maxHeight = Math.max( ...renderedElements.map((lines) => lines.length) ); const elementWidths = this.elements.map((el) => getWidth(el)); const paddedElements = renderedElements.map((lines, i) => { const width = elementWidths[i]; const paddedLines = [...lines]; while (paddedLines.length < maxHeight) { paddedLines.push(""); } return paddedLines.map((line) => line.padEnd(width, " ")); }); const result = []; for (let row2 = 0; row2 < maxHeight; row2++) { const rowContent = paddedElements.map((lines) => lines[row2]).join(" "); result.push(rowContent.trimEnd()); } return result.join("\n"); } }; var Layout = class { constructor(elements) { this.elements = elements; } render() { return this.elements.map((el) => el.render()).join("\n"); } }; var InlineBar = class { constructor(label, progress) { this.label = label; this.progress = progress; } render() { const clampedProgress = Math.max(0, Math.min(1, this.progress)); const filledSegments = Math.floor( clampedProgress * DIMENSIONS.PROGRESS_BAR_WIDTH ); const emptySegments = DIMENSIONS.PROGRESS_BAR_WIDTH - filledSegments; const bar = GLYPHS.BAR_FILLED.repeat(filledSegments) + GLYPHS.BAR_EMPTY.repeat(emptySegments); const percentage = Math.floor(clampedProgress * 100); return `${flattenToSingleLine(this.label)} [${bar}] ${percentage}%`; } }; var StatusCard = class _StatusCard { constructor(label, content, style = "single" /* Single */) { this.label = label; this.content = content; this.style = style; } /** * Set the border style (fluent API) */ border(style) { return new _StatusCard(this.label, this.content, style); } render() { const labelRendered = this.label.render(); const contentRendered = this.content.render(); const labelLines = labelRendered.split("\n"); const contentLines = contentRendered.split("\n"); const allLines = [...labelLines, ...contentLines]; const maxTextLength = allLines.length === 0 ? 0 : Math.max(...allLines.map((line) => stripAnsiCodes(line).length)); const contentWidth = maxTextLength + DIMENSIONS.MIN_CONTENT_PADDING; const chars = getBorderChars(this.style); const topBorder = chars.topLeft + chars.horizontal.repeat(contentWidth + 2) + chars.topRight; const bottomBorder = chars.bottomLeft + chars.horizontal.repeat(contentWidth + 2) + chars.bottomRight; const createCardLines = (lines) => lines.map((line) => { const visibleLength = stripAnsiCodes(line).length; const padding = contentWidth - visibleLength; return `${chars.vertical} ${line}${" ".repeat(padding)} ${chars.vertical}`; }); const labelCardLines = createCardLines(labelLines); const contentCardLines = createCardLines(contentLines); return [ topBorder, ...labelCardLines, ...contentCardLines, bottomBorder ].join("\n"); } }; var Table = class _Table { constructor(headers, rows, style = "single" /* Single */) { this.headers = headers; this.rows = rows; this.style = style; } /** * Set the border style (fluent API) */ border(style) { return new _Table(this.headers, this.rows, style); } render() { const headerLines = this.headers.map((h) => h.render().split("\n")); const rowLines = this.rows.map( (row2) => row2.map((cell) => cell.render().split("\n")) ); const allRowLines = [headerLines, ...rowLines]; const columnWidths = this.calculateColumnWidths(allRowLines); const chars = getBorderChars(this.style); const borders = this.createTableBorders(columnWidths, chars); const headerRowHeight = Math.max( ...headerLines.map((lines) => lines.length) ); const headerRows = this.buildMultilineTableRows( headerLines, columnWidths, headerRowHeight, chars ); const dataRows = rowLines.flatMap((row2) => { const rowHeight = Math.max(...row2.map((cellLines) => cellLines.length)); return this.buildMultilineTableRows(row2, columnWidths, rowHeight, chars); }); return [ borders.top, ...headerRows, borders.separator, ...dataRows, borders.bottom ].join("\n"); } calculateColumnWidths(allRowLines) { return this.headers.map((_, columnIndex) => { let maxWidth = 0; for (const row2 of allRowLines) { if (columnIndex < row2.length) { for (const line of row2[columnIndex]) { const width = stripAnsiCodes(line).length; if (width > maxWidth) maxWidth = width; } } } return maxWidth; }); } createTableBorders(widths, chars) { const segments = widths.map((w) => chars.horizontal.repeat(w)); const junctionChars = this.getJunctionChars(chars); return { top: segments.join(`${chars.horizontal}${junctionChars.teeDown}${chars.horizontal}`).replace(/^/, `${chars.topLeft}${chars.horizontal}`).replace(/$/, `${chars.horizontal}${chars.topRight}`), separator: segments.join(`${chars.horizontal}${junctionChars.cross}${chars.horizontal}`).replace(/^/, `${junctionChars.teeRight}${chars.horizontal}`).replace(/$/, `${chars.horizontal}${junctionChars.teeLeft}`), bottom: segments.join(`${chars.horizontal}${junctionChars.teeUp}${chars.horizontal}`).replace(/^/, `${chars.bottomLeft}${chars.horizontal}`).replace(/$/, `${chars.horizontal}${chars.bottomRight}`) }; } getJunctionChars(chars) { return { teeDown: "\u252C", teeUp: "\u2534", teeLeft: "\u2524", teeRight: "\u251C", cross: "\u253C" }; } buildMultilineTableRows(cellLines, widths, rowHeight, chars) { const result = []; for (let lineIndex = 0; lineIndex < rowHeight; lineIndex++) { const rowParts = cellLines.map((lines, colIndex) => { const line = lineIndex < lines.length ? lines[lineIndex] : ""; const visibleLength = stripAnsiCodes(line).length; const padding = widths[colIndex] - visibleLength; return line + " ".repeat(Math.max(0, padding)); }); result.push( `${chars.vertical} ${rowParts.join(` ${chars.vertical} `)} ${chars.vertical}` ); } return result; } }; function text(content) { return new Text(content); } function layout(...elements) { return new Layout(toElements(elements)); } function section(title, glyph = "=", flankingChars = 3) { return (content) => new Section(title, toElement(content), glyph, flankingChars); } function kv(...pairs) { return new KeyValue(pairs); } function ul(...items) { const elements = toElements(items); return new UnorderedList(elements); } function ol(...items) { return new OrderedList(items); } function box(title = "", style = "single" /* Single */) { return (...elements) => new Box(toElements(elements), title, style); } function row(...elements) { return new Row(toElements(elements)); } function hr(char = "\u2500", width) { return new HorizontalRule(char, width); } function inlineBar(label, progress) { return new InlineBar(toElement(label), progress); } function statusCard(label, content, style) { const labelElement = toElement(label); const contentElement = toElement(content); return new StatusCard( labelElement, contentElement, style ?? "single" /* Single */ ); } function toElement(item) { return typeof item === "string" ? new Text(item) : item; } function toElements(items) { return items.map(toElement); } function table(headers, rows, style) { const headerElements = toElements(headers); const rowElements = rows.map((row2) => toElements(row2)); return new Table(headerElements, rowElements, style ?? "single" /* Single */); } function br(count = 1) { if (count === 1) { return new LineBreak(); } return new Layout(Array(count).fill(new LineBreak())); } var Center = class { constructor(element, width) { this.element = element; this.width = width; } render() { const content = this.element.render(); const lines = content.split("\n"); const targetWidth = this.width ?? 80; return lines.map((line) => { const visibleLength = stripAnsiCodes(line).length; if (visibleLength >= targetWidth) { return line; } const padding = targetWidth - visibleLength; const leftPad = Math.floor(padding / 2); const rightPad = padding - leftPad; return " ".repeat(leftPad) + line + " ".repeat(rightPad); }).join("\n"); } }; function center(element, width) { return new Center(toElement(element), width); } function underline(char = "\u2500") { return (element) => new Underline(toElement(element), char); } function margin(prefix) { return (...elements) => new Margin(prefix, toElements(elements)); } function tree(label) { const baseTree = new Tree(label, []); const result = function(...children) { return new Tree(label, children); }; result.render = () => baseTree.render(); result.label = label; result.children = []; return result; } function chart(...data) { const chartData = data.map( ([label, value]) => [toElement(label), value] ); return new Chart(chartData); } function banner(content = "") { return new Banner(toElement(content), "double" /* Double */); } function columns(spacingOrFirstElement, ...elements) { if (typeof spacingOrFirstElement === "number") { return new Columns(toElements(elements), spacingOrFirstElement); } else { const allElements = [spacingOrFirstElement, ...elements]; return new Columns(toElements(allElements), 2); } } function pad(padding) { return (element) => new Padded(toElement(element), padding); } function truncate(maxWidth, ellipsis = "...") { return (element) => new Truncated(toElement(element), maxWidth, ellipsis); } function vr(lineCount, char = "\u2502") { return new VerticalRule(char, lineCount); } function wrap(maxWidth) { return (element) => new Wrapped(toElement(element), maxWidth); } function justify(targetWidth, justifyLastLine = false) { return (element) => new Justified(toElement(element), targetWidth, justifyLastLine); } function leftAlign(targetWidth) { return (element) => new LeftAligned(toElement(element), targetWidth); } function rightAlign(targetWidth) { return (element) => new RightAligned(toElement(element), targetWidth); } function autoCenter(element) { return new AutoCentered(toElement(element)); } function empty() { return new Empty(); } var margins = { error: (...elements) => new Margin("[\x1B[31merror\x1B[0m]", toElements(elements)), warn: (...elements) => new Margin("[\x1B[33mwarn\x1B[0m]", toElements(elements)), success: (...elements) => new Margin("[\x1B[32msuccess\x1B[0m]", toElements(elements)), info: (...elements) => new Margin("[\x1B[36minfo\x1B[0m]", toElements(elements)) }; var Underline = class { constructor(element, char = "\u2500") { this.element = element; this.char = char; } render() { const content = this.element.render(); const lines = content.split("\n"); const maxWidth = Math.max( ...lines.map((line) => stripAnsiCodes(line).length) ); let underlineStr = ""; while (underlineStr.length < maxWidth) { underlineStr += this.char; } underlineStr = underlineStr.substring(0, maxWidth); return content + "\n" + underlineStr; } }; var Margin = class { constructor(prefix, elements) { this.prefix = prefix; this.elements = elements; } render() { const content = this.elements.length === 1 ? this.elements[0] : new Layout(this.elements); const lines = content.render().split("\n"); return lines.map((line) => `${this.prefix} ${line}`).join("\n"); } }; var Chart = class { constructor(data) { this.data = data; } render() { if (this.data.length === 0) return ""; const maxValue = Math.max(...this.data.map(([, value]) => Math.abs(value))); if (maxValue === 0) return this.data.map(([label]) => label.render()).join("\n"); const maxLabelWidth = Math.max( ...this.data.map( ([label]) => Math.max( ...label.render().split("\n").map((line) => stripAnsiCodes(line).length) ) ) ); const labelWidth = Math.min( maxLabelWidth, DIMENSIONS.CHART_LABEL_MAX_WIDTH ); return this.data.map(([label, value]) => { const labelText = flattenToSingleLine(label); const truncatedLabel = stripAnsiCodes(labelText).length > labelWidth ? stripAnsiCodes(labelText).substring(0, labelWidth - 3) + "..." : labelText; const paddedLabel = truncatedLabel.padEnd(labelWidth, " "); const percentage = Math.abs(value) / maxValue; const barLength = Math.floor( percentage * DIMENSIONS.DEFAULT_CHART_WIDTH ); const bar = GLYPHS.BAR_FILLED.repeat(barLength); const emptyBar = GLYPHS.BAR_EMPTY.repeat( DIMENSIONS.DEFAULT_CHART_WIDTH - barLength ); const valueStr = typeof value === "number" && value % 1 === 0 ? value.toString() : value.toFixed(1); return `${paddedLabel} \u2502${bar}${emptyBar}\u2502 ${valueStr}`; }).join("\n"); } }; var Banner = class _Banner { constructor(content, style = "double" /* Double */) { this.content = content; this.style = style; } /** * Set the border style (fluent API) */ border(style) { return new _Banner(this.content, style); } render() { const rendered = this.content.render(); const lines = rendered.split("\n"); const maxWidth = lines.length === 0 ? 0 : Math.max(...lines.map((line) => stripAnsiCodes(line).length)); const contentWidth = maxWidth + DIMENSIONS.MIN_CONTENT_PADDING; const chars = getBorderChars(this.style); const topBorder = `${chars.topLeft}${chars.horizontal.repeat(contentWidth + 2)}${chars.topRight}`; const bottomBorder = `${chars.bottomLeft}${chars.horizontal.repeat(contentWidth + 2)}${chars.bottomRight}`; const contentLines = lines.map((line) => { const padding = contentWidth - stripAnsiCodes(line).length; return `${chars.vertical} ${line}${" ".repeat(padding)} ${chars.vertical}`; }); return [topBorder, ...contentLines, bottomBorder].join("\n"); } }; var Columns = class { constructor(elements, spacing = 2) { this.elements = elements; this.spacing = spacing; } render() { if (this.elements.length === 0) return ""; const renderedElements = this.elements.map((el) => el.render().split("\n")); const maxHeight = Math.max( ...renderedElements.map((lines) => lines.length) ); const elementWidths = this.elements.map((el) => getWidth(el)); const paddedElements = renderedElements.map((lines, i) => { const width = elementWidths[i]; const paddedLines = [...lines]; while (paddedLines.length < maxHeight) { paddedLines.push(""); } return paddedLines.map((line) => line.padEnd(width, " ")); }); const spacer = " ".repeat(this.spacing); const result = []; for (let row2 = 0; row2 < maxHeight; row2++) { const rowContent = paddedElements.map((lines) => lines[row2]).join(spacer); result.push(rowContent.trimEnd()); } return result.join("\n"); } }; var Padded = class { constructor(element, padding) { this.element = element; this.padding = padding; } render() { const content = this.element.render(); const lines = content.split("\n"); const maxWidth = lines.length === 0 ? 0 : Math.max(...lines.map((line) => stripAnsiCodes(line).length)); const horizontalPad = " ".repeat(this.padding); const verticalPad = " ".repeat(maxWidth + this.padding * 2); const paddedLines = lines.map((line) => { const linePadding = maxWidth - stripAnsiCodes(line).length; return `${horizontalPad}${line}${" ".repeat(linePadding)}${horizontalPad}`; }); const verticalLines = Array(this.padding).fill(verticalPad); return [...verticalLines, ...paddedLines, ...verticalLines].join("\n"); } }; var Truncated = class { constructor(element, maxWidth, ellipsis = "...") { this.element = element; this.maxWidth = maxWidth; this.ellipsis = ellipsis; } render() { const content = this.element.render(); const lines = content.split("\n"); return lines.map((line) => { const visibleLength = stripAnsiCodes(line).length; if (visibleLength <= this.maxWidth) { return line; } const truncateLength = this.maxWidth - this.ellipsis.length; if (truncateLength <= 0) { return this.ellipsis.substring(0, this.maxWidth); } const truncated = stripAnsiCodes(line).substring(0, truncateLength); return truncated + this.ellipsis; }).join("\n"); } }; var VerticalRule = class { constructor(char = "\u2502", lineCount) { this.char = char; this.lineCount = lineCount; } render() { const count = Math.max(1, this.lineCount); return Array(count).fill(this.char).join("\n"); } }; var Wrapped = class { constructor(element, maxWidth) { this.element = element; this.maxWidth = maxWidth; } render() { const content = this.element.render(); const lines = content.split("\n"); return lines.flatMap((line) => { const visibleLength = stripAnsiCodes(line).length; if (visibleLength <= this.maxWidth) { return [line]; } const plainLine = stripAnsiCodes(line); const words = plainLine.split(" "); const wrappedLines = []; let currentLine = ""; for (const word of words) { const testLine = currentLine ? `${currentLine} ${word}` : word; if (testLine.length <= this.maxWidth) { currentLine = testLine; } else { if (currentLine) { wrappedLines.push(currentLine); currentLine = word; } else { wrappedLines.push(word.substring(0, this.maxWidth)); currentLine = word.substring(this.maxWidth); } } } if (currentLine) { wrappedLines.push(currentLine); } return wrappedLines.length > 0 ? wrappedLines : [""]; }).join("\n"); } }; var Justified = class { constructor(element, targetWidth, justifyLastLine = false) { this.element = element; this.targetWidth = targetWidth; this.justifyLastLine = justifyLastLine; } render() { const content = this.element.render(); const lines = content.split("\n"); return lines.map((line, index) => { const plainLine = stripAnsiCodes(line).trim(); const visibleLength = plainLine.length; if (visibleLength >= this.targetWidth) { return plainLine.substring(0, this.targetWidth); } const isLastLine = index === lines.length - 1; if (isLastLine && !this.justifyLastLine) { return line; } const words = plainLine.split(" ").filter((word) => word.length > 0); if (words.length <= 1) { return plainLine.padEnd(this.targetWidth, " "); } const totalWordLength = words.join("").length; const totalSpaceNeeded = this.targetWidth - totalWordLength; const gaps = words.length - 1; if (gaps === 0) { return plainLine.padEnd(this.targetWidth, " "); } const spacePerGap = Math.floor(totalSpaceNeeded / gaps); const extraSpaces = totalSpaceNeeded % gaps; let result = ""; for (let i = 0; i < words.length; i++) { result += words[i]; if (i < words.length - 1) { const spaces = spacePerGap + (i < extraSpaces ? 1 : 0); result += " ".repeat(spaces); } } return result; }).join("\n"); } }; var LeftAligned = class { constructor(element, targetWidth) { this.element = element; this.targetWidth = targetWidth; } render() { const content = this.element.render(); const lines = content.split("\n"); return lines.map((line) => { const visibleLength = stripAnsiCodes(line).length; if (visibleLength >= this.targetWidth) { return stripAnsiCodes(line).substring(0, this.targetWidth); } return line + " ".repeat(this.targetWidth - visibleLength); }).join("\n"); } }; var RightAligned = class { constructor(element, targetWidth) { this.element = element; this.targetWidth = targetWidth; } render() { const content = this.element.render(); const lines = content.split("\n"); return lines.map((line) => { const visibleLength = stripAnsiCodes(line).length; if (visibleLength >= this.targetWidth) { return stripAnsiCodes(line).substring(0, this.targetWidth); } const padding = this.targetWidth - visibleLength; return " ".repeat(padding) + line; }).join("\n"); } }; var AutoCentered = class { constructor(element) { this.element = element; } render() { return this.element.render(); } }; var Empty = class { render() { return ""; } }; var dsl = { layout, text, box, section, banner, row, center, autoCenter, margin, columns, ul, ol, kv, table, tree, chart, statusCard, inlineBar, hr, vr, underline, br, pad, truncate, wrap, justify, leftAlign, rightAlign, empty, Border, BorderStyle, margins }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AutoCentered, Banner, Border, BorderStyle, Box, Center, Chart, Columns, DIMENSIONS, Empty, GLYPHS, HorizontalRule, InlineBar, Justified, KeyValue, Layout, LeftAligned, LineBreak, Margin, OrderedList, Padded, RightAligned, Row, Section, StatusCard, Table, Text, Tree, Truncated, Underline, UnorderedList, VerticalRule, Wrapped, autoCenter, banner, box, br, center, chart, columns, dsl, empty, getHeight, getWidth, hr, inlineBar, justify, kv, layout, leftAlign, margin, margins, ol, pad, rightAlign, row, section, statusCard, stripAnsiCodes, table, text, tree, truncate, ul, underline, vr, wrap });