Improve terminal Markdown rendering for wide mixed-language math/table output

Resolved 💬 2 comments Opened Jun 25, 2026 by cidxb Closed Jun 25, 2026

What variant of Codex are you using?

codex-cli 0.141.0

What feature would you like to see?

Title

Improve terminal Markdown rendering for wide mixed-language output, display math-like blocks, and tables

Body

What problem are you seeing?

Codex CLI already does some terminal formatting, but long mixed-language technical answers can become hard to read in the TUI, especially when the response contains:

  • Chinese + English mixed text
  • math-like LaTeX snippets
  • wide tables
  • long inline expressions
  • wrapped paragraphs inside a narrow terminal

This is mostly a rendering/readability issue. The content is correct, but the terminal presentation is sometimes much harder to scan than the same answer rendered as Markdown in a web UI.

One representative example. I am using an English example here to keep the issue easy to review, but the same rendering problem is more visible with CJK text and mixed-width characters:

A four-bar linkage is a simple example. Physically it only has 1 degree of freedom, but if it is described with 4 joint angles
(\theta_1,\theta_2,\theta_3,\theta_4), the configuration has to satisfy an additional loop-closure condition:

[
g(\theta)=0
]

This kind of constraint directly restricts the configuration, so it is a holonomic constraint. If the original configuration has
(n) variables and there are (k) independent holonomic constraints, the effective dimension is:

[
n-k
]

Taking the time derivative gives:

[
\frac{\partial g}{\partial \theta}\dot{\theta}=0
]

This can be written in the unified form:

[
A(\theta)\dot{\theta}=0
]

This is a Pfaffian constraint: a linear constraint at the velocity level.

The key point is that not every velocity constraint can be integrated back into a configuration constraint.

A car is the classic example. It can move forward, backward, and turn, but it cannot instantaneously slide sideways. Its velocity
constraint is:

[
\dot{x}\sin\phi-\dot{y}\cos\phi=0
]

Another related case is wide table output:

| Type | What it constrains | Does it reduce C-space dimension? | Example |
|---|---|---|---|
| configuration constraint / holonomic constraint | configuration itself (q) | yes | four-bar linkage loop closure |
| velocity constraint / nonholonomic constraint | velocity (\dot q) | not necessarily | a car cannot slide sideways |

In a narrow terminal, this kind of table is difficult to read if it is only wrapped as text or rendered as a very wide table.

Why this matters

Codex is often used for technical explanations, code reviews, math-heavy reasoning, and project planning. In those cases, the final answer may be correct but hard to consume because of terminal layout limitations.

The issue is especially visible for CJK users because terminal rendering has to account for display width, not just character count. Mixed Chinese, English, inline code, and math-like text can produce awkward wrapping.

Expected behavior

It would be helpful if Codex TUI could render final assistant messages with better terminal-aware Markdown handling:

  • Preserve math-like blocks instead of showing them as plain bracketed lines where possible.
  • Render GitHub-style Markdown tables more readably.
  • Use wcwidth-aware measurement for CJK/mixed-language text.
  • Fall back to a row-card layout when a Markdown table is wider than the terminal.
  • Preserve inline code and backslash-heavy expressions such as \dot q, \theta, and \frac{...}{...}.

For the table above, a narrow terminal fallback could be:

row 1
Type: configuration constraint / holonomic constraint
What it constrains: configuration itself (q)
Does it reduce C-space dimension?: yes
Example: four-bar linkage loop closure

row 2
Type: velocity constraint / nonholonomic constraint
What it constrains: velocity (\dot q)
Does it reduce C-space dimension?: not necessarily
Example: a car cannot slide sideways

This is usually easier to read than forcing a wide table into a narrow terminal.

Suggested implementation direction

I understand from the contributing guide that external code contributions are currently by invitation only, so I am opening this as a feature request / design suggestion rather than an unsolicited PR.

Possible low-risk rendering improvements:

  1. Detect GitHub-style Markdown tables in final assistant messages.
  2. Estimate table width using display width rather than string length.
  3. Render normally if the table fits the terminal.
  4. Fall back to row-card layout if the table is too wide.
  5. Preserve inline Markdown in cells where possible.
  6. Treat math-like bracket blocks as preformatted or display-math-style blocks.

The core heuristic could be:

if markdown_table_width <= terminal_width:
    render as terminal table
else:
    render each row as a key-value card

This would avoid making the normal case heavier while improving wide mixed-language tables.

Prototype

I tested a small local sidecar prototype that wraps codex exec --json, extracts the final assistant message, and renders it with Rich Markdown plus a custom Markdown table fallback:

codex exec --json ...
  -> final agent_message
  -> Markdown renderer
  -> wcwidth-aware table width estimate
  -> wide table fallback to row cards

This is not a replacement for the official TUI. It is only a proof of concept that the readability improvement is noticeable for mixed Chinese/English/math/table output.

The most useful part of the prototype was not “adding table borders”; Codex already has some terminal formatting. The useful part was:

  • final-answer re-rendering after generation completes
  • CJK display-width-aware table measurement
  • wide-table fallback to cards
  • preserving inline code and backslashes in math-like snippets

Environment

Please let me know what information would be most useful here. I can provide:

  • terminal width
  • terminal emulator
  • Codex CLI version
  • example prompts
  • before/after screenshots or text captures
  • a minimal fixture Markdown file for rendering tests

Related issues

This seems related to existing Markdown rendering requests such as:

  • Improve Markdown rendering
  • Support Markdown table rendering in Codex CLI
  • Render markdown for plan responses

I am happy to move this to an existing issue if that is preferred.

Additional information

_No response_

View original on GitHub ↗

2 Comments

alarcritty · 2 months ago

Thanks for the thorough write-up. A lot of what this issue asks for already exists in
codex-rs/tui, so here is a mapping of the asks onto what is shipped, plus the one real
gap and an honest note on the math piece.

## Already implemented in codex-rs/tui

The markdown renderer lives in tui/src/markdown_render.rs (built on pulldown-cmark)
with a key/value fallback in tui/src/markdown_render/table_key_value.rs. It already
covers most of the "Expected behavior" list:

  • Display-width / wcwidth-aware measurement. unicode-width

(UnicodeWidthStr / UnicodeWidthChar) is used throughout, and wrapping goes through
textwrap's display-width measurement. CJK and other wide characters are measured by
display width, not character count, so mixed-width text is accounted for.

  • GFM table detection and width-aware layout. Tables are parsed with

Options::ENABLE_TABLES. Columns are classified as Narrative (prose), TokenHeavy
(paths, URLs, hashes), or Compact (short values), and width is shrunk by priority so an
oversized path or URL gives up space before readable prose collapses.

  • Wide-table fallback to a row-card / key-value layout. When a grid can no longer be

presented in useful chunks, body rows are transposed into "Key: Value" records
separated by muted rules. This is exactly the
if table_width <= terminal_width: grid else: cards heuristic in your suggestion, with
some extra handling for partially starved cells.

  • Inline code and backslash preservation. Inline code spans render verbatim, so

\dot q, \theta, and \frac{...}{...} survive intact inside backticks.

This landed across #22052, #24489, #24636, and #24825.

So the table readability, wcwidth measurement, and row-card fallback parts of this
request look complete today. If you have a specific narrow-terminal input that still
overflows or wraps badly, a minimal fixture would be the most useful thing to attach,
since that can go straight into a snapshot test.

## The real gap is math, and it is larger than it looks

Two separate problems stack here:

  1. The parser has no math support on the pinned version. The repo is on

pulldown-cmark 0.10, which has no ENABLE_MATH option and no InlineMath /
DisplayMath events. Those arrived in 0.11. Adding $ / $$ math support therefore
requires bumping a shared workspace dependency to 0.11, which is a breaking change
(for example Tag::BlockQuote gains a BlockQuoteKind field, so existing match arms
in the renderer stop compiling) and re-verifying the renderer test and snapshot suite.
That is a maintainer call, not a drive-by change.

  1. The examples in this issue are not standard math delimiters anyway. The blocks use

bare [ ... ] and ( ... ) rather than $$ ... $$ or \[ ... \]. No CommonMark
parser treats bare brackets as math, so even after a 0.11 upgrade with math enabled,
this exact output would still render as plain text. Detecting it would need
bare-bracket heuristics, which risk false positives on ordinary prose like
"see step [3]".

The cleaner direction for the bracket case is to emit proper $$ delimiters upstream so
the standard path can handle it, rather than guessing at bare brackets in the renderer.

## Suggested next step

The one low-risk improvement that needs no dependency change is CJK / mixed-width
snapshot test coverage
. The width primitives are already correct, but there are
currently no CJK or full-width test cases, so these tests would either confirm the
existing behavior already satisfies the readability concern here, or surface a concrete
bug to fix. Either outcome closes the CJK part of this issue with evidence.

I understand contributions are invitation-only, so I am flagging this as a scoped
direction rather than opening a PR unprompted. Happy to put up the CJK tests if that
would be welcome, and to discuss whether a pulldown-cmark 0.11 upgrade is on the table
for math support.

cidxb · 2 months ago
Thanks for the thorough write-up. A lot of what this issue asks for already exists in codex-rs/tui, so here is a mapping of the asks onto what is shipped, plus the one real gap and an honest note on the math piece. ## Already implemented in codex-rs/tui The markdown renderer lives in tui/src/markdown_render.rs (built on pulldown-cmark) with a key/value fallback in tui/src/markdown_render/table_key_value.rs. It already covers most of the "Expected behavior" list: Display-width / wcwidth-aware measurement. unicode-width (UnicodeWidthStr / UnicodeWidthChar) is used throughout, and wrapping goes through textwrap's display-width measurement. CJK and other wide characters are measured by display width, not character count, so mixed-width text is accounted for. GFM table detection and width-aware layout. Tables are parsed with Options::ENABLE_TABLES. Columns are classified as Narrative (prose), TokenHeavy (paths, URLs, hashes), or Compact (short values), and width is shrunk by priority so an oversized path or URL gives up space before readable prose collapses. Wide-table fallback to a row-card / key-value layout. When a grid can no longer be presented in useful chunks, body rows are transposed into "Key: Value" records separated by muted rules. This is exactly the if table_width <= terminal_width: grid else: cards heuristic in your suggestion, with some extra handling for partially starved cells. Inline code and backslash preservation. Inline code spans render verbatim, so \dot q, \theta, and \frac{...}{...} survive intact inside backticks. This landed across #22052, #24489, #24636, and #24825. So the table readability, wcwidth measurement, and row-card fallback parts of this request look complete today. If you have a specific narrow-terminal input that still overflows or wraps badly, a minimal fixture would be the most useful thing to attach, since that can go straight into a snapshot test. ## The real gap is math, and it is larger than it looks Two separate problems stack here: 1. The parser has no math support on the pinned version. The repo is on pulldown-cmark 0.10, which has no ENABLE_MATH option and no InlineMath / DisplayMath events. Those arrived in 0.11. Adding $ / $$ math support therefore requires bumping a shared workspace dependency to 0.11, which is a breaking change (for example Tag::BlockQuote gains a BlockQuoteKind field, so existing match arms in the renderer stop compiling) and re-verifying the renderer test and snapshot suite. That is a maintainer call, not a drive-by change. 2. The examples in this issue are not standard math delimiters anyway. The blocks use bare [ ... ] and ( ... ) rather than $$ ... $$ or \[ ... \]. No CommonMark parser treats bare brackets as math, so even after a 0.11 upgrade with math enabled, this exact output would still render as plain text. Detecting it would need bare-bracket heuristics, which risk false positives on ordinary prose like "see step [3]". The cleaner direction for the bracket case is to emit proper $$ delimiters upstream so the standard path can handle it, rather than guessing at bare brackets in the renderer. ## Suggested next step The one low-risk improvement that needs no dependency change is CJK / mixed-width snapshot test coverage. The width primitives are already correct, but there are currently no CJK or full-width test cases, so these tests would either confirm the existing behavior already satisfies the readability concern here, or surface a concrete bug to fix. Either outcome closes the CJK part of this issue with evidence. I understand contributions are invitation-only, so I am flagging this as a scoped direction rather than opening a PR unprompted. Happy to put up the CJK tests if that would be welcome, and to discuss whether a pulldown-cmark 0.11 upgrade is on the table for math support.

Thanks for the detailed clarification. That answers my main technical questions.

It sounds like the table rendering, CJK/mixed-width measurement, inline code preservation, and wide-table key/value fallback are
already implemented in codex-rs/tui. The remaining example I noticed is mostly about math-like output sometimes being emitted as
bare bracket blocks, which is not standard Markdown math and should not be guessed by the renderer.

I’ll close this broad issue for now.

One broader UX note: even with the existing TUI renderer, Codex CLI output can still feel noticeably less readable than the web UI
for long technical answers, especially when the answer mixes prose, equations, tables, and code. I understand this may be partly
due to terminal constraints rather than missing renderer features. If I run into a concrete narrow-terminal fixture where the
current renderer behaves unexpectedly, I’ll attach a minimal repro or open a more specific issue.

Thanks again for the thorough explanation.