ASCII output: fullwidth (CJK/emoji) labels misalign box borders (grid assumes 1 cell = 1 display col)

Author: ivanmkcCreated Jun 1, 2026Updated Aug 29, 2026

Thanks for beautiful-mermaid! The ASCII renderer misaligns boxes when labels contain fullwidth / double-width characters (CJK, kana, hangul, fullwidth forms, emoji).

Environment: [email protected], renderMermaidASCII(src, { colorMode: 'none' }).

Repro:

graph LR
  A[日本語テスト] --> B[終了]

Output (borders too narrow for the text; right doesn't line up):

┌────────┐     ┌────┐
│ 日本語テスト ├────►│ 終了 │
└────────┘     └────┘

Root cause: the ASCII layer is a column-major grid where each cell holds exactly one code point (canvas[x][y]), and box width is measured by code-point count — src/ascii/multiline-utils.ts maxLineWidth returns Math.max(...lines.map(l => l.length)). A CJK/emoji glyph occupies one grid cell but two display columns, so:

  1. boxes are sized one cell per glyph (too narrow by ~1 col per wide char), and
  2. even if widened, the grid→string serialization writes one char per cell, so any row containing a wide glyph is visually one column wider than the grid believes — shifting everything to its right.

You already have an isFullwidth() in src/text-metrics.ts for the SVG path; the ASCII grid would need to (a) measure width with it and (b) reserve two cells per wide glyph (e.g. write the glyph + a skip/placeholder cell) across multiline-utils / canvas / draw so display width == cell count.

Happy to attempt a PR if you'd accept this approach. For now I've added a lint warning downstream so users know alignment will be off with CJK/emoji labels.

Source: lukilabs/beautiful-mermaid