Skip to content

Commit 45bccd3

Browse files
committed
fix permissions alignment
1 parent 404c126 commit 45bccd3

7 files changed

+280
-86
lines changed

codex-rs/tui/src/bottom_pane/chat_composer.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,16 @@ impl ChatComposer {
142142
.desired_height(width.saturating_sub(LIVE_PREFIX_COLS))
143143
+ match &self.active_popup {
144144
ActivePopup::None => FOOTER_HEIGHT_WITH_HINT,
145-
ActivePopup::Command(c) => c.calculate_required_height(),
145+
ActivePopup::Command(c) => c.calculate_required_height(width),
146146
ActivePopup::File(c) => c.calculate_required_height(),
147147
}
148148
}
149149

150150
pub fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
151151
let popup_constraint = match &self.active_popup {
152-
ActivePopup::Command(popup) => Constraint::Max(popup.calculate_required_height()),
152+
ActivePopup::Command(popup) => {
153+
Constraint::Max(popup.calculate_required_height(area.width))
154+
}
153155
ActivePopup::File(popup) => Constraint::Max(popup.calculate_required_height()),
154156
ActivePopup::None => Constraint::Max(FOOTER_HEIGHT_WITH_HINT),
155157
};
@@ -1232,7 +1234,10 @@ impl ChatComposer {
12321234
impl WidgetRef for ChatComposer {
12331235
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
12341236
let (popup_constraint, hint_spacing) = match &self.active_popup {
1235-
ActivePopup::Command(popup) => (Constraint::Max(popup.calculate_required_height()), 0),
1237+
ActivePopup::Command(popup) => (
1238+
Constraint::Max(popup.calculate_required_height(area.width)),
1239+
0,
1240+
),
12361241
ActivePopup::File(popup) => (Constraint::Max(popup.calculate_required_height()), 0),
12371242
ActivePopup::None => (
12381243
Constraint::Length(FOOTER_HEIGHT_WITH_HINT),

codex-rs/tui/src/bottom_pane/command_popup.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,35 @@ impl CommandPopup {
9292
.ensure_visible(matches_len, MAX_POPUP_ROWS.min(matches_len));
9393
}
9494

95-
/// Determine the preferred height of the popup. This is the number of
96-
/// rows required to show at most MAX_POPUP_ROWS commands.
97-
pub(crate) fn calculate_required_height(&self) -> u16 {
98-
self.filtered_items().len().clamp(1, MAX_POPUP_ROWS) as u16
95+
/// Determine the preferred height of the popup for a given width.
96+
/// Accounts for wrapped descriptions so that long tooltips don't overflow.
97+
pub(crate) fn calculate_required_height(&self, width: u16) -> u16 {
98+
use super::selection_popup_common::GenericDisplayRow;
99+
use super::selection_popup_common::measure_rows_height;
100+
let matches = self.filtered();
101+
let rows_all: Vec<GenericDisplayRow> = if matches.is_empty() {
102+
Vec::new()
103+
} else {
104+
matches
105+
.into_iter()
106+
.map(|(item, indices, _)| match item {
107+
CommandItem::Builtin(cmd) => GenericDisplayRow {
108+
name: format!("/{}", cmd.command()),
109+
match_indices: indices.map(|v| v.into_iter().map(|i| i + 1).collect()),
110+
is_current: false,
111+
description: Some(cmd.description().to_string()),
112+
},
113+
CommandItem::UserPrompt(i) => GenericDisplayRow {
114+
name: format!("/{}", self.prompts[i].name),
115+
match_indices: indices.map(|v| v.into_iter().map(|i| i + 1).collect()),
116+
is_current: false,
117+
description: Some("send saved prompt".to_string()),
118+
},
119+
})
120+
.collect()
121+
};
122+
123+
measure_rows_height(&rows_all, &self.state, MAX_POPUP_ROWS, width)
99124
}
100125

101126
/// Compute fuzzy-filtered matches over built-in commands and user prompts,

codex-rs/tui/src/bottom_pane/list_selection_view.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use super::bottom_pane_view::BottomPaneView;
1717
use super::popup_consts::MAX_POPUP_ROWS;
1818
use super::scroll_state::ScrollState;
1919
use super::selection_popup_common::GenericDisplayRow;
20+
use super::selection_popup_common::measure_rows_height;
2021
use super::selection_popup_common::render_rows;
2122

2223
/// One selectable item in the generic selection list.
@@ -135,11 +136,36 @@ impl BottomPaneView for ListSelectionView {
135136
CancellationEvent::Handled
136137
}
137138

138-
fn desired_height(&self, _width: u16) -> u16 {
139-
let rows = (self.items.len()).clamp(1, MAX_POPUP_ROWS);
139+
fn desired_height(&self, width: u16) -> u16 {
140+
// Measure wrapped height for up to MAX_POPUP_ROWS items at the given width.
141+
// Build the same display rows used by the renderer so wrapping math matches.
142+
let rows: Vec<GenericDisplayRow> = self
143+
.items
144+
.iter()
145+
.enumerate()
146+
.map(|(i, it)| {
147+
let is_selected = self.state.selected_idx == Some(i);
148+
let prefix = if is_selected { '>' } else { ' ' };
149+
let name_with_marker = if it.is_current {
150+
format!("{} (current)", it.name)
151+
} else {
152+
it.name.clone()
153+
};
154+
let display_name = format!("{} {}. {}", prefix, i + 1, name_with_marker);
155+
GenericDisplayRow {
156+
name: display_name,
157+
match_indices: None,
158+
is_current: it.is_current,
159+
description: it.description.clone(),
160+
}
161+
})
162+
.collect();
163+
164+
let rows_height = measure_rows_height(&rows, &self.state, MAX_POPUP_ROWS, width);
165+
140166
// +1 for the title row, +1 for a spacer line beneath the header,
141-
// +1 for optional subtitle, +1 for optional footer
142-
let mut height = rows as u16 + 2;
167+
// +1 for optional subtitle, +1 for optional footer (2 lines incl. spacing)
168+
let mut height = rows_height + 2;
143169
if self.subtitle.is_some() {
144170
// +1 for subtitle (the spacer is accounted for above)
145171
height = height.saturating_add(1);

0 commit comments

Comments
 (0)