mirror of
https://github.com/openai/codex.git
synced 2026-09-16 12:13:30 +00:00
feat: basic collab rendering
This commit is contained in:
@@ -24,6 +24,7 @@ use codex_core::protocol::AgentReasoningRawContentDeltaEvent;
|
||||
use codex_core::protocol::AgentReasoningRawContentEvent;
|
||||
use codex_core::protocol::ApplyPatchApprovalRequestEvent;
|
||||
use codex_core::protocol::BackgroundEventEvent;
|
||||
use codex_core::protocol::CollabInteractionEvent;
|
||||
use codex_core::protocol::CreditsSnapshot;
|
||||
use codex_core::protocol::DeprecationNoticeEvent;
|
||||
use codex_core::protocol::ErrorEvent;
|
||||
@@ -102,6 +103,7 @@ use crate::bottom_pane::SelectionViewParams;
|
||||
use crate::bottom_pane::custom_prompt_view::CustomPromptView;
|
||||
use crate::bottom_pane::popup_consts::standard_popup_hint_line;
|
||||
use crate::clipboard_paste::paste_image_to_temp_png;
|
||||
use crate::collab_event_cell;
|
||||
use crate::diff_render::display_path_for;
|
||||
use crate::exec_cell::CommandOutput;
|
||||
use crate::exec_cell::ExecCell;
|
||||
@@ -1073,6 +1075,11 @@ impl ChatWidget {
|
||||
self.set_status_header(message);
|
||||
}
|
||||
|
||||
fn on_collab_interaction(&mut self, event: CollabInteractionEvent) {
|
||||
self.add_to_history(collab_event_cell::new_collab_interaction(event));
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
fn on_undo_started(&mut self, event: UndoStartedEvent) {
|
||||
self.bottom_pane.ensure_status_indicator();
|
||||
self.bottom_pane.set_interrupt_hint_visible(false);
|
||||
@@ -2182,9 +2189,7 @@ impl ChatWidget {
|
||||
}
|
||||
EventMsg::ExitedReviewMode(review) => self.on_exited_review_mode(review),
|
||||
EventMsg::ContextCompacted(_) => self.on_agent_message("Context compacted".to_owned()),
|
||||
EventMsg::CollabInteraction(_) => {
|
||||
// TODO(jif) handle collab tools.
|
||||
}
|
||||
EventMsg::CollabInteraction(event) => self.on_collab_interaction(event),
|
||||
EventMsg::ThreadRolledBack(_) => {}
|
||||
EventMsg::RawResponseItem(_)
|
||||
| EventMsg::ItemStarted(_)
|
||||
|
||||
140
codex-rs/tui/src/collab_event_cell.rs
Normal file
140
codex-rs/tui/src/collab_event_cell.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use codex_core::protocol::AgentStatus;
|
||||
use codex_core::protocol::CollabInteractionEvent;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::Line;
|
||||
|
||||
use crate::history_cell::HistoryCell;
|
||||
use crate::text_formatting::truncate_text;
|
||||
use crate::wrapping::RtOptions;
|
||||
use crate::wrapping::word_wrap_lines;
|
||||
|
||||
const COLLAB_PROMPT_MAX_GRAPHEMES: usize = 120;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CollabInteractionCell {
|
||||
summary: Line<'static>,
|
||||
detail: Option<Line<'static>>,
|
||||
}
|
||||
|
||||
impl CollabInteractionCell {
|
||||
fn new(summary: Line<'static>, detail: Option<Line<'static>>) -> Self {
|
||||
Self { summary, detail }
|
||||
}
|
||||
}
|
||||
|
||||
impl HistoryCell for CollabInteractionCell {
|
||||
fn display_lines(&self, width: u16) -> Vec<Line<'static>> {
|
||||
let wrap_width = width.max(1) as usize;
|
||||
let mut lines = word_wrap_lines(
|
||||
std::iter::once(self.summary.clone()),
|
||||
RtOptions::new(wrap_width)
|
||||
.initial_indent("• ".dim().into())
|
||||
.subsequent_indent(" ".into()),
|
||||
);
|
||||
|
||||
if let Some(detail) = &self.detail {
|
||||
let detail_lines = word_wrap_lines(
|
||||
std::iter::once(detail.clone()),
|
||||
RtOptions::new(wrap_width)
|
||||
.initial_indent(" └ ".dim().into())
|
||||
.subsequent_indent(" ".into()),
|
||||
);
|
||||
lines.extend(detail_lines);
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
}
|
||||
|
||||
fn collab_status_label(status: &AgentStatus) -> String {
|
||||
match status {
|
||||
AgentStatus::PendingInit => "pending init".to_string(),
|
||||
AgentStatus::Running => "running".to_string(),
|
||||
AgentStatus::Completed(message) => format!("completed: {message:?}"),
|
||||
AgentStatus::Errored(_) => "errored".to_string(),
|
||||
AgentStatus::Shutdown => "shutdown".to_string(),
|
||||
AgentStatus::NotFound => "not found".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collab_detail_line(label: &str, message: &str) -> Option<Line<'static>> {
|
||||
let trimmed = message.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let collapsed = trimmed
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|line| !line.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
let truncated = truncate_text(&collapsed, COLLAB_PROMPT_MAX_GRAPHEMES);
|
||||
let label = format!("{label}: ");
|
||||
Some(Line::from(vec![label.dim(), truncated.into()]))
|
||||
}
|
||||
|
||||
pub(crate) fn new_collab_interaction(event: CollabInteractionEvent) -> CollabInteractionCell {
|
||||
let (summary, detail) = match event {
|
||||
CollabInteractionEvent::AgentSpawned { new_id, prompt, .. } => {
|
||||
let summary = Line::from(vec![
|
||||
"Spawned agent".bold(),
|
||||
" ".into(),
|
||||
new_id.to_string().dim(),
|
||||
]);
|
||||
let detail = collab_detail_line("Prompt", &prompt);
|
||||
(summary, detail)
|
||||
}
|
||||
CollabInteractionEvent::AgentInteraction {
|
||||
receiver_id,
|
||||
prompt,
|
||||
..
|
||||
} => {
|
||||
let summary = Line::from(vec![
|
||||
"Sent to agent".bold(),
|
||||
" ".into(),
|
||||
receiver_id.to_string().dim(),
|
||||
]);
|
||||
let detail = collab_detail_line("Message", &prompt);
|
||||
(summary, detail)
|
||||
}
|
||||
CollabInteractionEvent::WaitingBegin { receiver_id, .. } => {
|
||||
let summary = Line::from(vec![
|
||||
"Waiting on agent".bold(),
|
||||
" ".into(),
|
||||
receiver_id.to_string().dim(),
|
||||
]);
|
||||
(summary, None)
|
||||
}
|
||||
CollabInteractionEvent::WaitingEnd {
|
||||
receiver_id,
|
||||
status,
|
||||
..
|
||||
} => {
|
||||
let summary = Line::from(vec![
|
||||
"Wait ended for agent".bold(),
|
||||
" ".into(),
|
||||
receiver_id.to_string().dim(),
|
||||
" · ".dim(),
|
||||
collab_status_label(&status).dim(),
|
||||
]);
|
||||
(summary, None)
|
||||
}
|
||||
CollabInteractionEvent::Close {
|
||||
receiver_id,
|
||||
status,
|
||||
..
|
||||
} => {
|
||||
let summary = Line::from(vec![
|
||||
"Closed agent".bold(),
|
||||
" ".into(),
|
||||
receiver_id.to_string().dim(),
|
||||
" · ".dim(),
|
||||
collab_status_label(&status).dim(),
|
||||
]);
|
||||
(summary, None)
|
||||
}
|
||||
};
|
||||
|
||||
CollabInteractionCell::new(summary, detail)
|
||||
}
|
||||
@@ -43,6 +43,7 @@ mod bottom_pane;
|
||||
mod chatwidget;
|
||||
mod cli;
|
||||
mod clipboard_paste;
|
||||
mod collab_event_cell;
|
||||
mod color;
|
||||
pub mod custom_terminal;
|
||||
mod diff_render;
|
||||
|
||||
Reference in New Issue
Block a user