codex - ✅(Solved) Fix Slash command popup columns shift while scrolling [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#19499Fetched 2026-04-26 05:16:22
View on GitHub
Comments
1
Participants
2
Timeline
9
Reactions
0
Author
Timeline (top)
labeled ×3closed ×1commented ×1cross-referenced ×1

Fix Action

Fix / Workaround

I understand external pull requests are by invitation only, so I am opening this as an issue first with analysis and a small proposed patch. If this approach aligns with the intended fix, I would appreciate an invitation to submit a PR.

The following patch switches slash command popup measurement and rendering to AutoAllRows.

I have tested locally and it is fixed by this patch.

PR fix notes

PR #19511: Keep slash command popup columns stable while scrolling

Description (problem / solution / changelog)

Why

Fixes #19499.

The slash-command popup recalculated the command-name column from only the rows visible in the current viewport. That made the description column shift horizontally while scrolling through / commands whenever longer command names entered or left the visible window.

What Changed

codex-rs/tui/src/bottom_pane/command_popup.rs now uses the shared selection-popup AutoAllRows column-width mode for both height measurement and rendering. This keeps the command description column based on the full filtered slash-command list instead of the current viewport.

Verification

  • cargo test -p codex-tui bottom_pane::command_popup

Changed files

  • codex-rs/tui/src/bottom_pane/command_popup.rs (modified, +17/-4)

Code Example

/copy        copy last response as markdown
  /diff        show git diff (including untracked files)
  /mention     mention a file
  /status      show current session configuration and token usage
  /title       configure which items appear in the terminal title
  /statusline  configure which items appear in the status line
  /theme       choose a syntax highlighting theme
  /mcp         list configured MCP tools; use /mcp verbose for details

---

/permissions   choose what Codex is allowed to do
  /experimental  toggle experimental features
  /memories      configure memory use and generation
  /skills        use skills to improve how Codex performs specific tasks
  /review        review my current changes and find issues
  /rename        rename the current thread
  /new           start a new chat during a conversation
  /resume        resume a saved chat

---

diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs
index 28c749cb57..50aeed738d 100644
--- a/codex-rs/tui/src/bottom_pane/command_popup.rs
+++ b/codex-rs/tui/src/bottom_pane/command_popup.rs
@@ -4,8 +4,11 @@ use ratatui::widgets::WidgetRef;

 use super::popup_consts::MAX_POPUP_ROWS;
 use super::scroll_state::ScrollState;
+use super::selection_popup_common::ColumnWidthConfig;
+use super::selection_popup_common::ColumnWidthMode;
 use super::selection_popup_common::GenericDisplayRow;
-use super::selection_popup_common::render_rows;
+use super::selection_popup_common::measure_rows_height_with_col_width_mode;
+use super::selection_popup_common::render_rows_with_col_width_mode;
 use super::slash_commands;
 use crate::render::Insets;
 use crate::render::RectExt;
@@ -15,6 +18,10 @@ use crate::slash_command::SlashCommand;
 // `quit` is an alias of `exit`, so we skip `quit` here.
 // `approvals` is an alias of `permissions`.
 const ALIAS_COMMANDS: &[SlashCommand] = &[SlashCommand::Quit, SlashCommand::Approvals];
+const COMMAND_COLUMN_WIDTH: ColumnWidthConfig = ColumnWidthConfig::new(
+    ColumnWidthMode::AutoAllRows,
+    /*name_column_width*/ None,
+);

 /// A selectable item in the popup.
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -107,10 +114,15 @@ impl CommandPopup {
     /// Determine the preferred height of the popup for a given width.
     /// Accounts for wrapped descriptions so that long tooltips don't overflow.
     pub(crate) fn calculate_required_height(&self, width: u16) -> u16 {
-        use super::selection_popup_common::measure_rows_height;
         let rows = self.rows_from_matches(self.filtered());

-        measure_rows_height(&rows, &self.state, MAX_POPUP_ROWS, width)
+        measure_rows_height_with_col_width_mode(
+            &rows,
+            &self.state,
+            MAX_POPUP_ROWS,
+            width,
+            COMMAND_COLUMN_WIDTH,
+        )
     }

     /// Compute exact/prefix matches over built-in commands and user prompts,
@@ -221,7 +233,7 @@ impl CommandPopup {
 impl WidgetRef for CommandPopup {
     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
         let rows = self.rows_from_matches(self.filtered());
-        render_rows(
+        render_rows_with_col_width_mode(
             area.inset(Insets::tlbr(
                 /*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
             )),
@@ -230,6 +242,7 @@ impl WidgetRef for CommandPopup {
             &self.state,
             MAX_POPUP_ROWS,
             "no matches",
+            COMMAND_COLUMN_WIDTH,
         );
     }
 }
RAW_BUFFERClick to expand / collapse

What version of Codex CLI is running?

0.125.0, or latest main HEAD

What subscription do you have?

ChatGPT Plus

Which model were you using?

Unrelated

What platform is your computer?

Linux 6.19.11-arch1-1 x86_64 unknown

What terminal emulator and version are you using (if applicable)?

Windows Terminal

What issue are you seeing?

The TUI slash command popup recalculates the command-name column width from only the currently visible rows. As a result, the description column shifts left or right while scrolling through the / command list, depending on the longest command name visible in the current viewport.

What steps can reproduce the bug?

  1. Open the TUI.
  2. Type / to show the slash command popup.
  3. Scroll through the list.

One viewport may render with a narrower name column:

  /copy        copy last response as markdown
  /diff        show git diff (including untracked files)
  /mention     mention a file
  /status      show current session configuration and token usage
  /title       configure which items appear in the terminal title
  /statusline  configure which items appear in the status line
  /theme       choose a syntax highlighting theme
  /mcp         list configured MCP tools; use /mcp verbose for details

After scrolling, the same popup may render with a wider name column:

  /permissions   choose what Codex is allowed to do
  /experimental  toggle experimental features
  /memories      configure memory use and generation
  /skills        use skills to improve how Codex performs specific tasks
  /review        review my current changes and find issues
  /rename        rename the current thread
  /new           start a new chat during a conversation
  /resume        resume a saved chat

What is the expected behavior?

The description column should remain stable while scrolling. For a given filtered slash command list, the popup should reserve the width needed by the longest matching command name, not just the longest command visible in the current viewport.

Additional information

I understand external pull requests are by invitation only, so I am opening this as an issue first with analysis and a small proposed patch. If this approach aligns with the intended fix, I would appreciate an invitation to submit a PR.

CommandPopup currently uses the default selection popup row measurement and rendering helpers:

  • measure_rows_height
  • render_rows

Those use the default ColumnWidthMode::AutoVisible, which derives the left column width from visible rows only. This is useful for compact generic lists, but it causes visible layout movement in the slash command popup.

The shared selection popup code already supports ColumnWidthMode::AutoAllRows, which derives the column width from all rows and is intended for stable columns while scrolling.

The following patch switches slash command popup measurement and rendering to AutoAllRows.

diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs
index 28c749cb57..50aeed738d 100644
--- a/codex-rs/tui/src/bottom_pane/command_popup.rs
+++ b/codex-rs/tui/src/bottom_pane/command_popup.rs
@@ -4,8 +4,11 @@ use ratatui::widgets::WidgetRef;

 use super::popup_consts::MAX_POPUP_ROWS;
 use super::scroll_state::ScrollState;
+use super::selection_popup_common::ColumnWidthConfig;
+use super::selection_popup_common::ColumnWidthMode;
 use super::selection_popup_common::GenericDisplayRow;
-use super::selection_popup_common::render_rows;
+use super::selection_popup_common::measure_rows_height_with_col_width_mode;
+use super::selection_popup_common::render_rows_with_col_width_mode;
 use super::slash_commands;
 use crate::render::Insets;
 use crate::render::RectExt;
@@ -15,6 +18,10 @@ use crate::slash_command::SlashCommand;
 // `quit` is an alias of `exit`, so we skip `quit` here.
 // `approvals` is an alias of `permissions`.
 const ALIAS_COMMANDS: &[SlashCommand] = &[SlashCommand::Quit, SlashCommand::Approvals];
+const COMMAND_COLUMN_WIDTH: ColumnWidthConfig = ColumnWidthConfig::new(
+    ColumnWidthMode::AutoAllRows,
+    /*name_column_width*/ None,
+);

 /// A selectable item in the popup.
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -107,10 +114,15 @@ impl CommandPopup {
     /// Determine the preferred height of the popup for a given width.
     /// Accounts for wrapped descriptions so that long tooltips don't overflow.
     pub(crate) fn calculate_required_height(&self, width: u16) -> u16 {
-        use super::selection_popup_common::measure_rows_height;
         let rows = self.rows_from_matches(self.filtered());

-        measure_rows_height(&rows, &self.state, MAX_POPUP_ROWS, width)
+        measure_rows_height_with_col_width_mode(
+            &rows,
+            &self.state,
+            MAX_POPUP_ROWS,
+            width,
+            COMMAND_COLUMN_WIDTH,
+        )
     }

     /// Compute exact/prefix matches over built-in commands and user prompts,
@@ -221,7 +233,7 @@ impl CommandPopup {
 impl WidgetRef for CommandPopup {
     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
         let rows = self.rows_from_matches(self.filtered());
-        render_rows(
+        render_rows_with_col_width_mode(
             area.inset(Insets::tlbr(
                 /*top*/ 0, /*left*/ 2, /*bottom*/ 0, /*right*/ 0,
             )),
@@ -230,6 +242,7 @@ impl WidgetRef for CommandPopup {
             &self.state,
             MAX_POPUP_ROWS,
             "no matches",
+            COMMAND_COLUMN_WIDTH,
         );
     }
 }

I have tested locally and it is fixed by this patch.

extent analysis

TL;DR

The issue can be fixed by changing the ColumnWidthMode to AutoAllRows in the CommandPopup to reserve the width needed by the longest matching command name.

Guidance

  • The problem arises from the default ColumnWidthMode::AutoVisible which calculates the column width based on visible rows only.
  • To fix this, the ColumnWidthMode should be changed to AutoAllRows which calculates the column width based on all rows.
  • The proposed patch switches the CommandPopup measurement and rendering to AutoAllRows by using measure_rows_height_with_col_width_mode and render_rows_with_col_width_mode functions.
  • The COMMAND_COLUMN_WIDTH constant is introduced with ColumnWidthMode::AutoAllRows to configure the column width.

Example

const COMMAND_COLUMN_WIDTH: ColumnWidthConfig = ColumnWidthConfig::new(
    ColumnWidthMode::AutoAllRows,
    /*name_column_width*/ None,
);

Notes

The provided patch has been tested locally and fixes the issue. However, it's recommended to review the patch thoroughly and test it in different scenarios before applying it to the production code.

Recommendation

Apply the proposed patch to fix the issue, as it has been tested locally and addresses the root cause of the problem.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING