feat(tui): render cramped markdown tables as key-value records [2 of 2] (#24636)

## Stack

- **Base: #24489 [1 of 2]** - render markdown tables in app style.
- **Current: #24636 [2 of 2]** - render cramped markdown tables as
key/value records.

Review this PR against `fcoury/app-style-markdown-tables`; it contains
only the fallback behavior for cramped tables.

## Why

The row-separated markdown table rendering in #24489 remains readable
while columns have usable room. Once long links or multiple prose-heavy
columns are compressed into narrow allocations, however, the grid can
turn words and paths into tall vertical strips that are difficult to
scan. In those cases the content matters more than preserving the grid
shape.

## What Changed

<table>
<tr><td>
<p align="center"><b>
Normal
</b></p>
<img width="1722" height="619" alt="CleanShot 2026-05-27 at 14 32 57"
src="https://github.com/user-attachments/assets/d04f5fbd-6064-4acd-91bd-072d19b983df"
/>
</td></tr>
<tr><td>
<p align="center"><b>
Narrow
</b></p>
<img width="863" height="1013" alt="CleanShot 2026-05-27 at 14 33 12"
src="https://github.com/user-attachments/assets/6a7d2968-0a68-48fd-ab5d-209b3dbaf03e"
/>
</td></tr>
<tr><td>
<p align="center"><b>
Very narrow
</b></p>
<img width="435" height="746" alt="CleanShot 2026-05-27 at 14 33 47"
src="https://github.com/user-attachments/assets/f6a59e30-b1d2-4063-9c05-43933abc77d6"
/>
</td></tr>
</table>

- Detect tables whose grid allocation causes systemic token
fragmentation or starves multiple prose-heavy columns.
- Render those tables as repeated key/value records instead of retaining
an unreadable grid.
- Use aligned label/value records when there is useful horizontal room,
and switch to a stacked narrow-record layout where each label is
followed by a full-width value when width is especially constrained.
- Preserve the themed label color, rich inline formatting, links, and
the existing grid presentation for tables that remain readable.
- Add snapshot coverage for path-heavy narrow tables, prose-heavy issue
tables, systemic compact fragmentation, and a control case that should
continue to render as a grid.

## How to Test

1. Start Codex from this branch and render a normal multi-column
markdown table at a comfortable terminal width. Confirm it still appears
as the styled row-separated grid from #24489.
2. Render a table containing a long linked record identifier or
file-like value, then narrow the terminal until the grid would split the
value into vertical fragments. Confirm it switches to key/value records,
with labels above values at very narrow widths.
3. Render a table with multiple prose-heavy columns, such as an issue
summary table with `Issue`, `Activity`, `Complexity`, and `Why start`.
Confirm a cramped width switches to records rather than wrapping several
columns into hard-to-read strips.
4. Render a compact table where only one value wraps mildly. Confirm it
stays in grid form rather than switching prematurely.

## Validation

- Ran `just test -p codex-tui` while developing the fallback and
reviewed/accepted the intended new markdown-render snapshots. The
command still reports two unrelated existing guardian feature-flag test
failures outside this diff.
- Ran `just fix -p codex-tui` and `just fmt` after the Rust changes were
complete.
- `just argument-comment-lint` cannot reach source linting locally
because Bazel fails while resolving LLVM sanitizer headers; touched
positional literal callsites were inspected manually and annotated where
needed.
This commit is contained in:
Felipe Coury
2026-05-27 17:27:55 -03:00
committed by GitHub
Unverified
parent 7a26497836
commit 26c9502121
7 changed files with 528 additions and 32 deletions
+72 -28
View File
@@ -22,9 +22,9 @@
//! alignment count.
//! 3. **Compute column widths** -- allocate widths with content-aware
//! priority and iterative shrinking.
//! 4. **Render row-separated layout** -- theme-accented bold headers, a
//! heavier segmented header rule, and low-contrast segmented body
//! separators, or fallback to pipe format when the minimum cannot fit.
//! 4. **Choose presentation** -- render theme-accented row-separated columns
//! while values remain scannable, otherwise transpose body rows
//! into key/value records separated by muted rules.
//! 5. **Append spillover** -- extracted spillover rows rendered as plain text
//! after the table.
//!
@@ -34,8 +34,10 @@
//! or hashes), or Compact (short values such as counts and status labels).
//! Token-heavy columns give up excess width before narrative columns so an
//! oversized path does not collapse readable prose; compact values are
//! preserved last. When even 3-char-wide columns cannot fit, the table falls
//! back to pipe-delimited format.
//! preserved last. When compact values split, token-heavy values collapse into
//! unusably short chunks, expansive cells form tall narrow strips across enough
//! body rows, or even 3-char-wide columns cannot fit, body rows render as
//! key/value records.
use crate::render::highlight::foreground_style_for_scopes;
use crate::render::highlight::highlight_code_to_lines;
@@ -74,6 +76,8 @@ use unicode_width::UnicodeWidthChar;
use unicode_width::UnicodeWidthStr;
use url::Url;
mod table_key_value;
const TABLE_COLUMN_GAP: usize = 2;
const TABLE_CELL_PADDING: usize = 1;
const TABLE_HEADER_SEPARATOR_CHAR: char = '━';
@@ -233,8 +237,8 @@ impl TableState {
/// Rendered table output split by wrapping behavior.
///
/// `table_lines` are either prewrapped aligned rows or pipe
/// fallback rows that should still pass through normal wrapping.
/// `table_lines` are prewrapped aligned rows or key/value records, except
/// header-only tables may retain pipe fallback rows for normal wrapping.
/// `spillover_lines` are prose rows extracted from parser artifacts and should
/// be routed through normal wrapping.
struct RenderedTableLines {
@@ -285,11 +289,11 @@ pub fn render_markdown_text(input: &str) -> Text<'static> {
/// Render markdown constrained to a known terminal width.
///
/// The renderer preserves table structure when possible and falls back to
/// pipe-table output when an aligned table cannot fit the available width. Passing
/// `None` keeps intrinsic line widths and disables width-driven wrapping in the
/// markdown writer. Local file links render relative to the current process
/// working directory.
/// The renderer preserves columnar table structure while values remain
/// scannable and falls back to key/value records when body rows cannot fit
/// readably. Passing `None` keeps intrinsic line widths and disables
/// width-driven wrapping in the markdown writer. Local file links render
/// relative to the current process working directory.
pub(crate) fn render_markdown_text_with_width(input: &str, width: Option<usize>) -> Text<'static> {
let cwd = std::env::current_dir().ok();
render_markdown_text_with_width_and_cwd(input, width, cwd.as_deref())
@@ -1036,16 +1040,17 @@ where
}
}
/// Convert a completed `TableState` into styled, row-separated `Line`s.
/// Convert a completed `TableState` into styled table `Line`s.
///
/// Pipeline: filter spillover rows -> normalize column counts -> compute
/// column widths -> render aligned rows (or fall back to pipe format if the
/// minimum column widths exceed available terminal width). Spillover rows
/// are appended as plain text after the table.
/// column widths -> render aligned rows or key/value records when values
/// systemically lose token readability or expansive cells become tall
/// narrow strips. Spillover rows are appended as plain text after the
/// table.
///
/// Falls back to `render_table_pipe_fallback` (raw `| A | B |` format)
/// when `compute_column_widths` returns `None` (terminal too narrow for
/// even 3-char-wide columns).
/// Falls back to key/value records when body rows cannot fit in the aligned
/// grid; header-only tables retain raw pipe output because they contain no
/// records to transpose.
fn render_table_lines(&self, mut table_state: TableState) -> RenderedTableLines {
let column_count = table_state.alignments.len();
if column_count == 0 {
@@ -1081,6 +1086,7 @@ where
Self::normalize_row(row, column_count);
}
let metrics = Self::collect_table_column_metrics(&header, &rows, column_count);
let available_width = self.available_table_width(column_count);
let widths =
self.compute_column_widths(&header, &rows, &table_state.alignments, available_width);
@@ -1088,8 +1094,27 @@ where
.into_iter()
.flat_map(|spillover| spillover.lines)
.collect();
let header_style =
foreground_style_for_scopes(&["entity.name.type", "support.type", "variable"])
.unwrap_or(self.styles.strong)
.bold();
let separator_style = table_separator_style();
let Some(column_widths) = widths else {
if !rows.is_empty() {
return RenderedTableLines {
table_lines: table_key_value::render_records(
&header,
&rows,
&metrics,
self.available_record_width(),
header_style,
separator_style,
),
table_lines_prewrapped: true,
spillover_lines,
};
}
return RenderedTableLines {
table_lines: self.render_table_pipe_fallback(
&header,
@@ -1101,11 +1126,21 @@ where
};
};
let header_style =
foreground_style_for_scopes(&["entity.name.type", "support.type", "variable"])
.unwrap_or(self.styles.strong)
.bold();
let separator_style = table_separator_style();
if table_key_value::should_render_records(&rows, &column_widths, &metrics) {
return RenderedTableLines {
table_lines: table_key_value::render_records(
&header,
&rows,
&metrics,
self.available_record_width(),
header_style,
separator_style,
),
table_lines_prewrapped: true,
spillover_lines,
};
}
let mut out = Vec::with_capacity(2 + rows.len() * 2);
out.extend(self.render_table_row(
&header,
@@ -1157,6 +1192,15 @@ where
})
}
/// Return the full content budget for record fallback rendering.
fn available_record_width(&self) -> Option<usize> {
self.wrap_width.map(|wrap_width| {
let prefix_width =
Self::spans_display_width(&self.prefix_spans(self.pending_marker_line));
wrap_width.saturating_sub(prefix_width)
})
}
/// Allocate column widths for aligned, row-separated table rendering.
///
/// Each column starts at its natural (max cell content) width, then columns
@@ -1447,11 +1491,11 @@ where
out
}
/// Render the table as raw pipe-delimited lines (`| A | B |`).
/// Render a header-only table as raw pipe-delimited lines (`| A | B |`).
///
/// Used when `compute_column_widths` returns `None` (terminal too narrow
/// for even 3-char-wide columns). Pipe characters inside cell content are
/// escaped as `\|` so downstream parsers keep cell boundaries intact.
/// Used when `compute_column_widths` returns `None` and there are no body
/// records to transpose. Pipe characters inside cell content are escaped
/// as `\|` so downstream parsers keep cell boundaries intact.
fn render_table_pipe_fallback(
&self,
header: &[TableCell],
@@ -0,0 +1,254 @@
//! Vertical key/value rendering for markdown tables that no longer scan well as grids.
use super::TABLE_BODY_SEPARATOR_CHAR;
use super::TableCell;
use super::TableColumnKind;
use super::TableColumnMetrics;
use crate::render::line_utils::push_owned_lines;
use crate::wrapping::RtOptions;
use crate::wrapping::word_wrap_line;
use ratatui::style::Style;
use ratatui::text::Line;
use ratatui::text::Span;
use unicode_width::UnicodeWidthStr;
const FIELD_LEADING_PADDING: usize = 1;
const FIELD_GAP: usize = 2;
const MIN_VALUE_WIDTH: usize = 3;
const MIN_ALIGNED_COMPACT_VALUE_WIDTH: usize = 12;
const MIN_ALIGNED_EXPANSIVE_VALUE_WIDTH: usize = 24;
const MIN_SCANNABLE_NARRATIVE_WIDTH: usize = 12;
const MIN_SCANNABLE_TOKEN_HEAVY_WIDTH: usize = 12;
const CRAMPED_EXPANSIVE_CELL_LINES: usize = 4;
const CATASTROPHIC_NARRATIVE_CELL_LINES: usize = 7;
const STACKED_VALUE_INDENT: usize = 2;
/// Switch modes after enough records contain values the grid can no longer
/// present in useful chunks or expansive content collapses into tall strips.
pub(super) fn should_render_records(
rows: &[Vec<TableCell>],
column_widths: &[usize],
metrics: &[TableColumnMetrics],
) -> bool {
if rows.is_empty() {
return false;
}
let affected_rows = rows
.iter()
.filter(|row| {
let contains_fragmented_value =
row.iter()
.zip(column_widths)
.zip(metrics)
.any(|((cell, width), metrics)| {
let has_fragmented_token = cell
.plain_text()
.split_whitespace()
.any(|token| token.width() > *width);
match metrics.kind {
TableColumnKind::Compact => has_fragmented_token,
TableColumnKind::TokenHeavy => {
*width < MIN_SCANNABLE_TOKEN_HEAVY_WIDTH && has_fragmented_token
}
TableColumnKind::Narrative => false,
}
});
contains_fragmented_value || expansive_cells_are_starved(row, column_widths, metrics)
})
.count();
let threshold = if rows.len() == 1 {
1
} else {
2.max(rows.len().div_ceil(3))
};
affected_rows >= threshold
}
fn expansive_cells_are_starved(
row: &[TableCell],
column_widths: &[usize],
metrics: &[TableColumnMetrics],
) -> bool {
let expansive_cells: Vec<(TableColumnKind, usize, usize)> = row
.iter()
.zip(column_widths)
.zip(metrics)
.filter(|&((_cell, _width), metrics)| metrics.kind != TableColumnKind::Compact)
.map(|((cell, width), metrics)| (metrics.kind, *width, wrap_cell(cell, *width).len()))
.collect();
expansive_cells
.iter()
.filter(|(_, _, height)| *height >= CRAMPED_EXPANSIVE_CELL_LINES)
.count()
>= 2
|| expansive_cells.iter().any(|(kind, width, height)| {
*kind == TableColumnKind::Narrative
&& *width < MIN_SCANNABLE_NARRATIVE_WIDTH
&& *height >= CATASTROPHIC_NARRATIVE_CELL_LINES
})
}
pub(super) fn render_records(
headers: &[TableCell],
rows: &[Vec<TableCell>],
metrics: &[TableColumnMetrics],
available_width: Option<usize>,
label_style: Style,
separator_style: Style,
) -> Vec<Line<'static>> {
let label_width = headers
.iter()
.map(|header| header.plain_text().width())
.max()
.unwrap_or(0);
let minimum_value_width = if metrics
.iter()
.any(|metrics| metrics.kind != TableColumnKind::Compact)
{
MIN_ALIGNED_EXPANSIVE_VALUE_WIDTH
} else {
MIN_ALIGNED_COMPACT_VALUE_WIDTH
};
let aligned_fields = available_width.is_none_or(|width| {
FIELD_LEADING_PADDING + label_width + FIELD_GAP + minimum_value_width <= width
});
let mut out = Vec::new();
for (row_index, row) in rows.iter().enumerate() {
for (header, value) in headers.iter().zip(row) {
if aligned_fields {
render_aligned_field(
&mut out,
header,
value,
label_width,
available_width,
label_style,
);
} else {
render_stacked_field(&mut out, header, value, available_width, label_style);
}
}
if row_index + 1 < rows.len() {
let width = available_width.unwrap_or_else(|| widest_line_width(&out));
out.push(Line::from(Span::styled(
TABLE_BODY_SEPARATOR_CHAR.to_string().repeat(width),
separator_style,
)));
}
}
out
}
fn render_aligned_field(
out: &mut Vec<Line<'static>>,
header: &TableCell,
value: &TableCell,
label_width: usize,
available_width: Option<usize>,
label_style: Style,
) {
let value_indent = FIELD_LEADING_PADDING + label_width + FIELD_GAP;
let value_width = available_width
.map(|width| width.saturating_sub(value_indent).max(MIN_VALUE_WIDTH))
.unwrap_or_else(|| cell_width(value).max(MIN_VALUE_WIDTH));
let wrapped_value = wrap_cell(value, value_width);
for (line_index, value_line) in wrapped_value.into_iter().enumerate() {
let mut spans = Vec::new();
if line_index == 0 {
let label = header.plain_text();
spans.push(Span::raw(" ".repeat(FIELD_LEADING_PADDING)));
spans.push(Span::styled(label.clone(), label_style));
spans.push(Span::raw(
" ".repeat(label_width.saturating_sub(label.width()) + FIELD_GAP),
));
} else {
spans.push(Span::raw(" ".repeat(value_indent)));
}
spans.extend(value_line.spans);
out.push(Line::from(spans));
}
}
fn render_stacked_field(
out: &mut Vec<Line<'static>>,
header: &TableCell,
value: &TableCell,
available_width: Option<usize>,
label_style: Style,
) {
let label_width = available_width
.map(|width| width.saturating_sub(FIELD_LEADING_PADDING).max(1))
.unwrap_or_else(|| header.plain_text().width().max(1));
let label = Line::from(Span::styled(header.plain_text(), label_style));
let mut wrapped_labels = Vec::new();
push_owned_lines(
&word_wrap_line(&label, RtOptions::new(label_width)),
&mut wrapped_labels,
);
for label_line in wrapped_labels {
let mut spans = vec![Span::raw(" ".repeat(FIELD_LEADING_PADDING))];
spans.extend(label_line.spans);
out.push(Line::from(spans));
}
let value_width = available_width
.map(|width| width.saturating_sub(STACKED_VALUE_INDENT).max(1))
.unwrap_or_else(|| cell_width(value).max(1));
for value_line in wrap_cell(value, value_width) {
let mut spans = vec![Span::raw(" ".repeat(STACKED_VALUE_INDENT))];
spans.extend(value_line.spans);
out.push(Line::from(spans));
}
}
fn wrap_cell(cell: &TableCell, width: usize) -> Vec<Line<'static>> {
if cell.lines.is_empty() {
return vec![Line::default()];
}
let mut wrapped = Vec::new();
for source_line in &cell.lines {
let rendered = word_wrap_line(source_line, RtOptions::new(width.max(1)));
if rendered.is_empty() {
wrapped.push(Line::default());
} else {
push_owned_lines(&rendered, &mut wrapped);
}
}
if wrapped.is_empty() {
wrapped.push(Line::default());
}
wrapped
}
fn cell_width(cell: &TableCell) -> usize {
cell.lines
.iter()
.map(|line| {
line.spans
.iter()
.map(|span| span.content.width())
.sum::<usize>()
})
.max()
.unwrap_or(0)
}
fn widest_line_width(lines: &[Line<'_>]) -> usize {
lines
.iter()
.map(|line| {
line.spans
.iter()
.map(|span| span.content.width())
.sum::<usize>()
})
.max()
.unwrap_or(0)
}
+85 -4
View File
@@ -1582,6 +1582,60 @@ fn table_wraps_file_paths_before_collapsing_narrative_columns_snapshot() {
assert_snapshot!(plain_lines(&text).join("\n"));
}
#[test]
fn table_renders_stacked_key_value_records_when_path_column_becomes_too_narrow_snapshot() {
let md = r#"| Session | Why useful | Detected table blocks |
| --- | --- | --- |
| [2026-05-25 current gallery](/Users/felipe.coury/.codex/sessions/2026/05/25/rollout-2026-05-25T18-13-09-019e60fc-0518-7c21-9596-980fe97225ba.jsonl) | The large gallery from this thread: emojis, links, emphasis, code, alignment, paragraphs, and a 30+ row table | 7 |
| [2026-05-14 renderer testing](/Users/felipe.coury/.codex/sessions/2026/05/14/rollout-2026-05-14T12-57-18-019e2734-e500-7011-8278-975c94d06000.jsonl) | Explicit "markdown tables for testing" session with several successive assistant samples | 16 |
| [2026-05-14 five-table test](/Users/felipe.coury/.codex/sessions/2026/05/14/rollout-2026-05-14T12-27-57-019e271a-064c-78c3-a5cd-a6f20a0c1ad5.jsonl) | Explicit request for five tables containing emojis, code, italics, and varied cell content | 10 |
"#;
let text = render_markdown_text_with_width(md, Some(/*width*/ 42));
assert_snapshot!(plain_lines(&text).join("\n"));
}
#[test]
fn table_renders_records_when_multiple_prose_columns_are_starved_snapshot() {
let md = r#"| Issue | Activity | Complexity | Why start |
| --- | ---: | ---: | --- |
| [#24485: newline shortcut fails in PyCharm terminal on Windows](https://github.com/openai/codex/issues/24485) | `+1` 0, substantive comments 0 | Low | New, deterministic regression range; localized composer/keymap path. |
| [#23926: Vim composer `e` stalls at word end](https://github.com/openai/codex/issues/23926) | `+1` 0, comments 0 | Low | Standing best quick win; deterministic motion bug. |
| [#23651: Zellij scrollback misses Codex transcript over SSH](https://github.com/openai/codex/issues/23651) | `+1` 3, human comments 2 | Medium | Clear regression and strong scrollback evidence. |
| [#23740: raw ANSI/control sequences in Windows Terminal](https://github.com/openai/codex/issues/23740) | `+1` 7, human comments 7 | Medium | Highest activity; established Windows rendering regression family. |
| [#24527: typing lag increases with session length](https://github.com/openai/codex/issues/24527) | `+1` 0, substantive comments 0 | Medium | New TUI-visible performance report; needs profiling before implementation. |
"#;
let text = render_markdown_text_with_width(md, Some(/*width*/ 76));
assert_snapshot!(plain_lines(&text).join("\n"));
}
#[test]
fn table_keeps_grid_when_only_one_compact_record_fragments_snapshot() {
let md = r#"| Key | Date | State |
| --- | --- | --- |
| short | 2025-01-01 | Ready |
| verylongidentifier | 2025-02-02 | Ready |
| final | 2025-03-03 | Done |
"#;
let text = render_markdown_text_with_width(md, Some(/*width*/ 40));
assert_snapshot!(plain_lines(&text).join("\n"));
}
#[test]
fn table_renders_key_value_records_when_compact_fragmentation_is_systemic_snapshot() {
let md = r#"| Key | Notes |
| --- | --- |
| firstlongid | A readable explanatory sentence for this row. |
| secondlongid | Another readable explanatory sentence for this row. |
| short | A final readable explanatory sentence for this row. |
"#;
let text = render_markdown_text_with_width(md, Some(/*width*/ 17));
assert_snapshot!(plain_lines(&text).join("\n"));
}
#[test]
fn table_inside_blockquote_has_quote_prefix() {
let md = "> | A | B |\n> |---|---|\n> | 1 | 2 |\n";
@@ -1610,19 +1664,46 @@ fn escaped_pipes_render_in_table_cells() {
}
#[test]
fn table_falls_back_to_pipe_rendering_if_it_cannot_fit() {
fn table_falls_back_to_key_value_records_if_grid_cannot_fit() {
let md = "| c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c9 | c10 |\n|---|---|---|---|---|---|---|---|---|---|\n| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |\n";
let text = crate::markdown_render::render_markdown_text_with_width(md, Some(20));
let text = crate::markdown_render::render_markdown_text_with_width(md, Some(/*width*/ 20));
let lines: Vec<String> = text
.lines
.iter()
.map(|line| line.spans.iter().map(|span| span.content.clone()).collect())
.collect();
assert!(lines.first().is_some_and(|line| line.starts_with('|')));
assert!(lines.first().is_some_and(|line| line.contains("c1")));
assert!(lines.iter().any(|line| line.contains("c10") && line.contains("10")));
assert!(
!lines
.iter()
.any(|line| line.contains('━') || line.contains('─'))
.any(|line| line.starts_with('|') || line.contains('━') || line.contains('─'))
);
}
#[test]
fn table_key_value_fallback_preserves_rich_values_and_themed_labels() {
let md = "| Key | Content | Extra | More |\n|---|---|---|---|\n| item | [link](https://example.com) | **bold** | `code` |\n";
let text = crate::markdown_render::render_markdown_text_with_width(md, Some(/*width*/ 16));
let lines = plain_lines(&text);
assert!(lines.iter().any(|line| line.contains("Key")));
assert!(lines.iter().any(|line| line.contains("item")));
assert!(lines.iter().any(|line| line.contains("link")));
assert!(lines.iter().any(|line| line.contains("bold")));
assert!(lines.iter().any(|line| line.contains("code")));
assert!(
text.lines[0]
.spans
.iter()
.any(|span| span.content.contains("Key")
&& span.style.add_modifier.contains(Modifier::BOLD)
&& span.style.fg.is_some())
);
assert!(text.lines.iter().any(|line| {
line.spans
.iter()
.any(|span| span.style.add_modifier.contains(Modifier::UNDERLINED))
}));
}
@@ -0,0 +1,12 @@
---
source: tui/src/markdown_render_tests.rs
expression: "plain_lines(&text).join(\"\\n\")"
---
Key Date State
━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━ ━━━━━━━
short 2025-01-01 Ready
───────────────── ──────────── ───────
verylongidentif 2025-02-02 Ready
ier
───────────────── ──────────── ───────
final 2025-03-03 Done
@@ -0,0 +1,29 @@
---
source: tui/src/markdown_render_tests.rs
expression: "plain_lines(&text).join(\"\\n\")"
---
Key
firstlongid
Notes
A readable
explanatory
sentence for
this row.
─────────────────
Key
secondlongid
Notes
Another
readable
explanatory
sentence for
this row.
─────────────────
Key
short
Notes
A final
readable
explanatory
sentence for
this row.
@@ -0,0 +1,36 @@
---
source: tui/src/markdown_render_tests.rs
expression: "plain_lines(&text).join(\"\\n\")"
---
Issue #24485: newline shortcut fails in PyCharm terminal on Windows
(https://github.com/openai/codex/issues/24485)
Activity +1 0, substantive comments 0
Complexity Low
Why start New, deterministic regression range; localized composer/keymap
path.
────────────────────────────────────────────────────────────────────────────
Issue #23926: Vim composer e stalls at word end (https://github.com/
openai/codex/issues/23926)
Activity +1 0, comments 0
Complexity Low
Why start Standing best quick win; deterministic motion bug.
────────────────────────────────────────────────────────────────────────────
Issue #23651: Zellij scrollback misses Codex transcript over SSH
(https://github.com/openai/codex/issues/23651)
Activity +1 3, human comments 2
Complexity Medium
Why start Clear regression and strong scrollback evidence.
────────────────────────────────────────────────────────────────────────────
Issue #23740: raw ANSI/control sequences in Windows Terminal
(https://github.com/openai/codex/issues/23740)
Activity +1 7, human comments 7
Complexity Medium
Why start Highest activity; established Windows rendering regression
family.
────────────────────────────────────────────────────────────────────────────
Issue #24527: typing lag increases with session length (https://
github.com/openai/codex/issues/24527)
Activity +1 0, substantive comments 0
Complexity Medium
Why start New TUI-visible performance report; needs profiling before
implementation.
@@ -0,0 +1,40 @@
---
source: tui/src/markdown_render_tests.rs
expression: "plain_lines(&text).join(\"\\n\")"
---
Session
/Users/felipe.coury/.codex/
sessions/2026/05/25/rollout-2026-05-
25T18-13-09-019e60fc-0518-7c21-9596-
980fe97225ba.jsonl
Why useful
The large gallery from this thread:
emojis, links, emphasis, code,
alignment, paragraphs, and a 30+ row
table
Detected table blocks
7
──────────────────────────────────────────
Session
/Users/felipe.coury/.codex/
sessions/2026/05/14/rollout-2026-05-
14T12-57-18-019e2734-e500-7011-8278-
975c94d06000.jsonl
Why useful
Explicit "markdown tables for testing"
session with several successive
assistant samples
Detected table blocks
16
──────────────────────────────────────────
Session
/Users/felipe.coury/.codex/
sessions/2026/05/14/rollout-2026-05-
14T12-27-57-019e271a-064c-78c3-a5cd-
a6f20a0c1ad5.jsonl
Why useful
Explicit request for five tables
containing emojis, code, italics, and
varied cell content
Detected table blocks
10