tui: normalize pending steer keys directly

This commit is contained in:
Charles Cunningham
2026-03-02 16:14:52 -08:00
parent 76c2da4f96
commit 90b671fbea
3 changed files with 76 additions and 11 deletions
-1
View File
@@ -86,7 +86,6 @@ use codex_protocol::config_types::Settings;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::items::AgentMessageContent;
use codex_protocol::items::AgentMessageItem;
use codex_protocol::items::TurnItem;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::local_image_label_text;
use codex_protocol::parse_command::ParsedCommand;
+34 -10
View File
@@ -1,6 +1,4 @@
use super::*;
use codex_core::parse_turn_item;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::protocol::ConversationStartParams;
use codex_protocol::protocol::RealtimeAudioFrame;
use codex_protocol::protocol::RealtimeConversationClosedEvent;
@@ -92,14 +90,40 @@ impl ChatWidget {
pub(super) fn rendered_user_message_event_from_inputs(
items: &[UserInput],
) -> RenderedUserMessageEvent {
let response_item = ResponseInputItem::from(items.to_vec()).into();
let Some(TurnItem::UserMessage(user_message)) = parse_turn_item(&response_item) else {
unreachable!("user inputs must round-trip into a user-message turn item");
};
let EventMsg::UserMessage(event) = user_message.as_legacy_event() else {
unreachable!("UserMessageItem::as_legacy_event must return UserMessage");
};
Self::rendered_user_message_event_from_event(&event)
let mut message = String::new();
let mut remote_image_urls = Vec::new();
let mut local_images = Vec::new();
let mut text_elements = Vec::new();
for item in items {
match item {
UserInput::Text {
text,
text_elements: current_text_elements,
} => {
let offset = message.len();
for element in current_text_elements {
text_elements.push(TextElement::new(
(offset + element.byte_range.start..offset + element.byte_range.end)
.into(),
element.placeholder(text).map(str::to_string),
));
}
message.push_str(text);
}
UserInput::Image { image_url } => remote_image_urls.push(image_url.clone()),
UserInput::LocalImage { path } => local_images.push(path.clone()),
UserInput::Skill { .. } | UserInput::Mention { .. } => {}
_ => {}
}
}
Self::rendered_user_message_event_from_parts(
message,
text_elements,
local_images,
remote_image_urls,
)
}
pub(super) fn should_render_realtime_user_message_event(
+42
View File
@@ -3612,6 +3612,48 @@ async fn live_legacy_agent_message_after_item_completed_does_not_duplicate_assis
assert!(drain_insert_history(&mut rx).is_empty());
}
#[test]
fn rendered_user_message_event_from_inputs_matches_flattened_user_message_shape() {
let local_image = PathBuf::from("/tmp/local.png");
let rendered = ChatWidget::rendered_user_message_event_from_inputs(&[
UserInput::Text {
text: "hello ".to_string(),
text_elements: vec![TextElement::new((0..5).into(), None)],
},
UserInput::Image {
image_url: "https://example.com/remote.png".to_string(),
},
UserInput::LocalImage {
path: local_image.clone(),
},
UserInput::Skill {
name: "demo".to_string(),
path: PathBuf::from("/tmp/skill/SKILL.md"),
},
UserInput::Mention {
name: "repo".to_string(),
path: "app://repo".to_string(),
},
UserInput::Text {
text: "world".to_string(),
text_elements: vec![TextElement::new((0..5).into(), Some("planet".to_string()))],
},
]);
assert_eq!(
rendered,
ChatWidget::rendered_user_message_event_from_parts(
"hello world".to_string(),
vec![
TextElement::new((0..5).into(), Some("hello".to_string())),
TextElement::new((6..11).into(), Some("planet".to_string())),
],
vec![local_image],
vec!["https://example.com/remote.png".to_string()],
)
);
}
#[tokio::test]
async fn item_completed_only_pops_front_pending_steer() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;