(width: number)
| 149 | } |
| 150 | |
| 151 | render(width: number): string[] { |
| 152 | // Check cache |
| 153 | if (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) { |
| 154 | return this.cachedLines; |
| 155 | } |
| 156 | |
| 157 | // Calculate available width for content (subtract horizontal padding) |
| 158 | const contentWidth = Math.max(1, width - this.paddingX * 2); |
| 159 | |
| 160 | // Don't render anything if there's no actual text |
| 161 | if (!this.text || this.text.trim() === "") { |
| 162 | const result: string[] = []; |
| 163 | // Update cache |
| 164 | this.cachedText = this.text; |
| 165 | this.cachedWidth = width; |
| 166 | this.cachedLines = result; |
| 167 | return result; |
| 168 | } |
| 169 | |
| 170 | // Replace tabs with 3 spaces for consistent rendering |
| 171 | const normalizedText = this.text.replace(/\t/g, " "); |
| 172 | |
| 173 | // Parse markdown to HTML-like tokens |
| 174 | const tokens = markdownParser.lexer(normalizedText); |
| 175 | trimPartialClosingFences(tokens); |
| 176 | |
| 177 | // Convert tokens to styled terminal output |
| 178 | const renderedLines: string[] = []; |
| 179 | |
| 180 | for (let i = 0; i < tokens.length; i++) { |
| 181 | const token = tokens[i]!; |
| 182 | const nextToken = tokens[i + 1]; |
| 183 | const tokenLines = this.renderToken(token, contentWidth, nextToken?.type); |
| 184 | for (const tokenLine of tokenLines) { |
| 185 | renderedLines.push(tokenLine); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | // Wrap lines (NO padding, NO background yet) |
| 190 | const wrappedLines: string[] = []; |
| 191 | for (const line of renderedLines) { |
| 192 | if (isImageLine(line)) { |
| 193 | wrappedLines.push(line); |
| 194 | } else { |
| 195 | for (const wrappedLine of wrapTextWithAnsi(line, contentWidth)) { |
| 196 | wrappedLines.push(wrappedLine); |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | // Add margins and background to each wrapped line |
| 202 | const leftMargin = " ".repeat(this.paddingX); |
| 203 | const rightMargin = " ".repeat(this.paddingX); |
| 204 | const bgFn = this.defaultTextStyle?.bgColor; |
| 205 | const contentLines: string[] = []; |
| 206 | |
| 207 | for (const line of wrappedLines) { |
| 208 | if (isImageLine(line)) { |
nothing calls this directly
no test coverage detected