fix: improve markdown rendering with proper wide character support

This commit is contained in:
mitchell 2026-05-12 14:16:07 -04:00
parent 91175c70b6
commit af52e89928
2 changed files with 29 additions and 12 deletions

View file

@ -48,10 +48,12 @@ export default class Renderer {
private checkContainment(text: string, pipe: WriteStream = process.stdout) {
const [columns, rows] = pipe.getWindowSize();
this.charsSinceNL += text.length;
this.charsSinceNL += Bun.stringWidth(text);
if (text.includes("\n")) {
this.lineCount += text.match(/\n/g)?.length ?? 1;
this.charsSinceNL = text.length - text.lastIndexOf("\n") - 1;
this.charsSinceNL =
Bun.stringWidth(text) -
Bun.stringWidth(text.slice(0, text.lastIndexOf("\n")));
} else if (this.charsSinceNL > columns) {
this.lineCount++;
this.charsSinceNL = this.charsSinceNL - columns;

View file

@ -1,4 +1,4 @@
import { Table } from "@cliffy/table";
import { Cell, Table } from "@cliffy/table";
import chalk from "chalk";
import {
type BundledLanguage,
@ -12,6 +12,12 @@ import { codeToANSI } from "./codeToANSI";
let highlighter: HighlighterGeneric<BundledLanguage, BundledTheme> | undefined;
let highligherLangs = "";
class SWCell extends Cell {
public override get length() {
return Bun.stringWidth(this.toString(), { ambiguousIsNarrow: true });
}
}
export async function renderMarkdown(
text: PromiseLike<string> | string,
columns: number,
@ -32,8 +38,8 @@ export async function renderMarkdown(
} catch {}
}
let header: string[] = [];
let body: string[][] = [];
let header: SWCell[] = [];
let body: SWCell[][] = [];
const rendered = Bun.markdown.render(md, {
heading: (children, { level }) => {
@ -58,7 +64,8 @@ export async function renderMarkdown(
const lines = Bun.wrapAnsi(`\n${children.trim()}`, columns - 2, {
trim: false,
})
.replace(/\n([a-zA-Z-_]+)\n/g, "\n$1")
.replace(/\n(\s?[a-zA-Z_-]+\s?)\n/g, "\n$1")
.replace(/\n+/g, "\n")
.concat("\n")
.split("\n");
const toRender = lines
@ -86,7 +93,7 @@ export async function renderMarkdown(
list: (children, { depth }) => {
const trimmed = children
.replace(/\n+/g, "\n")
.replace(/\n([a-zA-Z-_]+)\n/g, "\n$1");
.replace(/\n(\s?[a-zA-Z_-]+\s?)\n/g, "\n$1");
return depth === 0 ? `${trimmed}\n` : `\n${trimmed}`;
},
listItem: (children, { index, depth, ordered, start, checked }) => {
@ -113,20 +120,28 @@ export async function renderMarkdown(
.header(header)
.body(body)
.border()
.maxColWidth(Math.floor(columns / header.length) - header.length)
.maxColWidth(
Math.floor(columns / header.length) - Math.floor(header.length / 2),
)
.toString();
header = [];
body = [];
return `${table}\n\n`;
},
tr: (children) => {
if (children) body.push(children.split(":tablesep:").slice(1));
return "";
if (!children) return;
body.push(
children
.split(":tablesep:")
.slice(1)
.map((value) => new SWCell(value)),
);
return null;
},
td: (children) => `:tablesep:${children}`,
th: (children) => {
header.push(chalk.red(children));
return "";
header.push(new SWCell(chalk.red(children)));
return null;
},
strong: (children) => chalk.bold(children),
emphasis: (children) => chalk.italic(children),