From a887e1b185d9d415a0e4e623d78da675a28b76ea Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 25 May 2026 23:18:40 -0400 Subject: [PATCH] fix: improve markdown rendering whitespace and list numbering --- src/render/markdown.ts | 57 +++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/render/markdown.ts b/src/render/markdown.ts index a745b82..9ccb4b0 100644 --- a/src/render/markdown.ts +++ b/src/render/markdown.ts @@ -41,7 +41,7 @@ class LmcRenderer extends Renderer { const color = colors[depth - 1] ?? chalk.white; const text = this.parser.parseInline(tokens); return wrapAnsi( - color.bold(`${"#".repeat(depth)} ${text}\n\n`), + color.bold(`${"#".repeat(depth)} ${text}\n`), this.columns, { trim: false }, ); @@ -49,23 +49,19 @@ class LmcRenderer extends Renderer { override paragraph({ tokens }: Tokens.Paragraph) { const text = this.parser.parseInline(tokens); - return wrapAnsi(`${text}\n\n`, this.columns, { trim: false }); + return wrapAnsi(`\n${text}\n`, this.columns, { trim: false }); } override blockquote({ tokens }: Tokens.Blockquote) { const text = this.parser.parse(tokens).trim(); - const lines = wrapAnsi(text, this.columns - 2, { trim: false }) - .replace(/\n+/g, "\n") - .concat("\n") - .split("\n"); - const toRender = lines - .map((line) => `> ${line}`) - .join("\n") - .concat("\n\n"); - return toRender.replaceAll(">", chalk.dim(">")); + const lines = wrapAnsi(`\n${text}\n`, this.columns - 2, { + trim: false, + }).split("\n"); + const toRender = lines.map((line) => `> ${line}`).join("\n"); + return `${toRender.replaceAll(">", chalk.dim(">"))}\n`; } - override code({ text, lang, escaped: _ }: Tokens.Code) { + override code({ text, lang }: Tokens.Code) { let toRender = text; if (highlighter && lang) { @@ -79,20 +75,26 @@ class LmcRenderer extends Renderer { } catch {} } - return `\`\`\`${chalk.blue(lang ?? "")}\n${toRender}\`\`\`\n`; + return `\n\`\`\`${chalk.blue(lang ?? "")}\n${toRender}\n\`\`\`\n`; } - override list({ items }: Tokens.List) { - const renderedItems = items.map((item) => this.listitem(item)).join(""); - const trimmed = renderedItems.replace(/\n+/g, "\n"); - return `${trimmed}\n`; + override list({ items, ordered, start }: Tokens.List) { + const num = start || 1; + const renderedItems = items + .map((item, index) => + this.listitem(item, ordered ? num + index : undefined), + ) + .join("\n"); + return `\n${renderedItems}\n`.replaceAll("\n\n", "\n"); } - override listitem({ task, checked, tokens }: Tokens.ListItem) { + override listitem({ task, checked, tokens }: Tokens.ListItem, num?: number) { const text = this.parser.parse(tokens); let marker = ""; if (task) { marker = checked ? "• [✓]" : "• [ ]"; + } else if (num !== undefined) { + marker = `${num}.`; } else { marker = "•"; } @@ -101,12 +103,11 @@ class LmcRenderer extends Renderer { return wrapAnsi(`${marker} ${text}`, cols, { trim: false }) .split("\n") .map((line) => (line.length > 0 ? ` ${line}` : "")) - .join("\n") - .concat("\n"); + .join("\n"); } override hr() { - return `${chalk.dim("─".repeat(this.columns))}\n\n`; + return `\n${chalk.dim("─".repeat(this.columns))}\n`; } override table({ header, rows }: Tokens.Table) { @@ -126,10 +127,10 @@ class LmcRenderer extends Renderer { .header(headerCells) .body(body) .border() - .maxColWidth(Math.floor((this.columns - 3) / headerCells.length)) + .maxColWidth(Math.floor((this.columns - 4) / headerCells.length)) .toString(); - return `${table}\n\n`; + return `\n${table}\n`; } override strong({ tokens }: Tokens.Strong) { @@ -157,7 +158,7 @@ class LmcRenderer extends Renderer { } override image({ text: _ }: Tokens.Image) { - return chalk.red("Images are unsupported (for now)\n"); + return chalk.red("\nImages are unsupported (for now)\n"); } override text(token: Tokens.Text) { @@ -175,7 +176,11 @@ class LmcRenderer extends Renderer { } override html({ text: _ }: Tokens.HTML | Tokens.Tag) { - return chalk.red("HTML is unsupported\n"); + return chalk.red("\nHTML is unsupported\n"); + } + + override br() { + return "\n"; } } @@ -207,5 +212,5 @@ export async function renderMarkdown( const rendered = await marked.parse(md); - return rendered.replace(/\n\n$/, "\n"); + return rendered.trimStart(); }