mirror of
https://github.com/openai/codex.git
synced 2026-11-05 16:29:31 +00:00
Cache TUI flex heights across frame passes (#34348)
## Why Sizing, rendering, and cursor placement can query the same chat widget layout multiple times in one frame, repeatedly measuring active transcript cells. ## What changed - Build one chat widget renderable tree per frame and reuse it for sizing, rendering, and cursor placement. - Cache each flex child's desired height by width for the lifetime of that tree. - Reuse the bottom pane's renderable directly instead of forwarding each renderable operation through a wrapper. ## Testing - Verify flex layouts measure a child once across frame passes and remeasure it when the width changes. - Verify a chat widget frame measures its active transcript cell once. GitOrigin-RevId: 5ad1a6711f4011c699b5d002b13dc3319cb4db8e
This commit is contained in:
1 parent
ada5a79ddf
commit
eceb3eeaf3
7 files changed
+141
-90
No files matched your search
@@ -3,7 +3,7 @@
|
||||
use super::*;
|
||||
|
||||
impl ChatWidget {
|
||||
pub(super) fn as_renderable(&self) -> RenderableItem<'_> {
|
||||
pub(crate) fn as_renderable(&self) -> RenderableItem<'_> {
|
||||
let active_cell_right_reserve = self.ambient_pet_wrap_reserved_cols();
|
||||
let active_cell_renderable = match &self.transcript.active_cell {
|
||||
Some(cell) => RenderableItem::Owned(Box::new(TranscriptAreaRenderable {
|
||||
@@ -48,42 +48,17 @@ impl ChatWidget {
|
||||
}
|
||||
flex.push(
|
||||
/*flex*/ 0,
|
||||
RenderableItem::Owned(Box::new(BottomPaneComposerReserveRenderable {
|
||||
bottom_pane: &self.bottom_pane,
|
||||
right_reserve: active_cell_right_reserve,
|
||||
}))
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
self.bottom_pane
|
||||
.as_renderable_with_composer_right_reserve(active_cell_right_reserve)
|
||||
.inset(Insets::tlbr(
|
||||
/*top*/ 1, /*left*/ 0, /*bottom*/ 0, /*right*/ 0,
|
||||
)),
|
||||
);
|
||||
RenderableItem::Owned(Box::new(flex))
|
||||
}
|
||||
}
|
||||
|
||||
struct BottomPaneComposerReserveRenderable<'a> {
|
||||
bottom_pane: &'a BottomPane,
|
||||
right_reserve: u16,
|
||||
}
|
||||
|
||||
impl Renderable for BottomPaneComposerReserveRenderable<'_> {
|
||||
fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.bottom_pane
|
||||
.render_with_composer_right_reserve(area, buf, self.right_reserve);
|
||||
}
|
||||
|
||||
fn desired_height(&self, width: u16) -> u16 {
|
||||
self.bottom_pane
|
||||
.desired_height_with_composer_right_reserve(width, self.right_reserve)
|
||||
}
|
||||
|
||||
fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
|
||||
self.bottom_pane
|
||||
.cursor_pos_with_composer_right_reserve(area, self.right_reserve)
|
||||
}
|
||||
|
||||
fn cursor_style(&self, area: Rect) -> crossterm::cursor::SetCursorStyle {
|
||||
self.bottom_pane
|
||||
.cursor_style_with_composer_right_reserve(area, self.right_reserve)
|
||||
pub(crate) fn note_rendered_width(&self, width: u16) {
|
||||
self.last_rendered_width.set(Some(width as usize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +107,7 @@ impl TranscriptAreaRenderable<'_> {
|
||||
impl Renderable for ChatWidget {
|
||||
fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.as_renderable().render(area, buf);
|
||||
self.last_rendered_width.set(Some(area.width as usize));
|
||||
self.note_rendered_width(area.width);
|
||||
}
|
||||
|
||||
fn desired_height(&self, width: u16) -> u16 {
|
||||
|
||||
@@ -215,6 +215,10 @@ pub(super) async fn make_chatwidget_manual_with_auth(
|
||||
(widget, rx, op_rx)
|
||||
}
|
||||
|
||||
pub(crate) fn set_active_cell(chat: &mut ChatWidget, cell: Box<dyn HistoryCell>) {
|
||||
chat.transcript.active_cell = Some(cell);
|
||||
}
|
||||
|
||||
// ChatWidget may emit other `Op`s (e.g. history/logging updates) on the same channel; this helper
|
||||
// filters until we see a submission op.
|
||||
pub(super) fn next_submit_op(op_rx: &mut tokio::sync::mpsc::UnboundedReceiver<Op>) -> Op {
|
||||
|
||||
Reference in new issue
Block a user