Skip to content

feat(tui): render responsive Markdown tables in TUI#22052

Merged
fcoury-oai merged 3 commits into
mainfrom
fcoury/md-table
May 10, 2026
Merged

feat(tui): render responsive Markdown tables in TUI#22052
fcoury-oai merged 3 commits into
mainfrom
fcoury/md-table

Conversation

@fcoury-oai
Copy link
Copy Markdown
Contributor

@fcoury-oai fcoury-oai commented May 10, 2026

Why

The TUI currently treats Markdown tables as ordinary wrapped text, which makes table-heavy responses hard to read and brittle across narrow panes and terminal resizes.

This change teaches the TUI to render Markdown tables responsively while preserving the raw Markdown source needed to re-render streamed and finalized transcript content after width changes. The goal is to keep tables legible during streaming, after resize, and once a turn has finished, without corrupting scrollback ordering.

What Changed

  • add table detection and responsive table rendering in the Markdown renderer
  • render standard tables with Unicode box-drawing borders when the pane is wide enough
  • add a vertical readability fallback for constrained or dense tables so narrow panes still show each row clearly
  • keep links and <br> content inside table cells instead of leaking text outside the table
  • avoid table normalization inside fenced or indented code blocks
  • preserve raw streamed Markdown source and keep the active table as a mutable tail until finalization
  • consolidate finalized streamed content into source-backed transcript cells so post-resize re-rendering stays correct
  • add snapshot and targeted streaming/resize regression coverage for the new table behavior

How to Test

  1. Start Codex TUI from this branch.
  2. Paste this exact prompt:
    This is a session to test codex, no need to do any thinking, just end different markdown tables, with columns exploring different markdown contents, like links, bold italic, code, etc. Make them different sizes, some 30+ rows, some not and intertwine them with some paragraphs with complex formatting as well.
  3. Confirm the response includes several Markdown tables mixed with richly formatted paragraphs.
  4. Confirm wide-enough tables render with box-drawing borders instead of plain wrapped pipe text.
  5. Resize the terminal narrower while the answer is still streaming and confirm the in-progress table stays coherent instead of duplicating headers or leaving broken scrollback behind.
  6. Resize again after the turn finishes and confirm the finalized transcript re-renders cleanly at the new width.
  7. In a narrow pane, verify dense tables fall back to the vertical per-row layout instead of producing unreadable wrapped columns.
  8. Also verify pipe-heavy fenced code blocks still render as code, not as tables.

Targeted tests:

  • cargo test -p codex-tui table_readability_fallback --no-fail-fast
  • cargo test -p codex-tui markdown_render --no-fail-fast
  • cargo test -p codex-tui streaming::controller --no-fail-fast
  • cargo test -p codex-tui table_resize_lifecycle --no-fail-fast

Docs

No developer docs update appears necessary.

Copy link
Copy Markdown
Collaborator

@etraut-openai etraut-openai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! This is going to make a lot of TUI users happy.

In addition to reviewing the code, I tested using multiple terminals (terminal.app, iTerm2, Kitty, Ghostty, Codex app terminal). I also tested many different sizes and shapes of markdown tables and different widths and heights and color themes.

Overall, it worked well. I found a few issues that I consider edge cases. I think we could ship the current implementation without addressing these. Items 4 and 5 look like legit bugs that users could hit.

  1. When I resize the window to narrower during streaming a markdown table, I sometimes see the table redraw over the top of existing content without replacing that content in areas where there is "white space" within the table. The problem corrects itself once the table is fully streamed.
  2. I manually disabled the terminal_resize_reflow feature flag, and the new functionality (not surprisingly) doesn't work well when resize reflow is disabled. Since this is on by default, I don't think this is a big deal. We will probably deprecate the terminal_resize_reflow flag soon anyway.
  3. When the terminal is narrower, sometimes that markdown table doesn't render as a table and instead renders as the raw markdown characters. For example, I asked the agent to emit a table with 30 columns. At a typical terminal width, this doesn't render as a table. I'm not sure whether this is an intentional design choice or a bug. It seems like a reasonable fallback when the table is too wide.
  4. Codex found a bug related to plan mode results that are presented as a table. They render correctly during streaming but then are rendered as raw markdown text after streaming completes. To repro, use this prompt in plan mode:
Please only propose a plan and then stop. Do not inspect files or run commands.
In the proposed plan, include this exact fenced markdown table as the main plan content:

```md
| Step | Owner |
|---|---|
| Write tests | Agent |
| Verify output | User |
```
  1. Codex also found a bug in the spillover detection logic. To repro, use the following prompt:
Reply with exactly this markdown table and no other text:

| Name | Notes | Status |
|---|---|---|
| First | normal | ok |
| HTML block: | | |
  1. A few of the code files (markdown_renderer.rs and streaming/controller.rs) are getting really large. We may want to break these into smaller submodules. This can wait for future PRs.

@fcoury-oai
Copy link
Copy Markdown
Contributor Author

Nice! This is going to make a lot of TUI users happy.

Thank you for the review!

In addition to reviewing the code, I tested using multiple terminals (terminal.app, iTerm2, Kitty, Ghostty, Codex app terminal). I also tested many different sizes and shapes of markdown tables and different widths and heights and color themes.

Overall, it worked well. I found a few issues that I consider edge cases. I think we could ship the current implementation without addressing these. Items 4 and 5 look like legit bugs that users could hit.

I agree, let's ship this as soon as I rebase from main.

  1. When I resize the window to narrower during streaming a markdown table, I sometimes see the table redraw over the top of existing content without replacing that content in areas where there is "white space" within the table. The problem corrects itself once the table is fully streamed.

Noted.

  1. I manually disabled the terminal_resize_reflow feature flag, and the new functionality (not surprisingly) doesn't work well when resize reflow is disabled. Since this is on by default, I don't think this is a big deal. We will probably deprecate the terminal_resize_reflow flag soon anyway.

Yes, I agree with the judgement here. We could keep old behavior in this case, but since we are deprecating it anyways, may not be worth the trouble.

  1. When the terminal is narrower, sometimes that markdown table doesn't render as a table and instead renders as the raw markdown characters. For example, I asked the agent to emit a table with 30 columns. At a typical terminal width, this doesn't render as a table. I'm not sure whether this is an intentional design choice or a bug. It seems like a reasonable fallback when the table is too wide.

I think we should have a fallback that renders the table with each column as a row and separate each row. I have that ready and will send a subsequent PR.

  1. Codex found a bug related to plan mode results that are presented as a table. They render correctly during streaming but then are rendered as raw markdown text after streaming completes. To repro, use this prompt in plan mode:
Please only propose a plan and then stop. Do not inspect files or run commands.
In the proposed plan, include this exact fenced markdown table as the main plan content:

```md
| Step | Owner |
|---|---|
| Write tests | Agent |
| Verify output | User |

This is a bad omission, let me evaluate how hard it is to include this one on this PR and I'll make a decision.

  1. Codex also found a bug in the spillover detection logic. To repro, use the following prompt:
Reply with exactly this markdown table and no other text:

| Name | Notes | Status |
|---|---|---|
| First | normal | ok |
| HTML block: | | |

This one might be possible to fix as well, let me take a look.

  1. A few of the code files (markdown_renderer.rs and streaming/controller.rs) are getting really large. We may want to break these into smaller submodules. This can wait for future PRs.

Yes, let's add this to the follow-up candidates.

@fcoury-oai fcoury-oai enabled auto-merge (squash) May 10, 2026 20:33
@fcoury-oai fcoury-oai merged commit 5248e3d into main May 10, 2026
26 checks passed
@fcoury-oai fcoury-oai deleted the fcoury/md-table branch May 10, 2026 20:42
@github-actions github-actions Bot locked and limited conversation to collaborators May 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants