mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
adding fork information (UI) when forking (#10246)
- shows `/fork` command that ran in prev session - shows `session forked from name (uuid) || uuid (if name is not set)` as an event in new session
This commit is contained in:
committed by
GitHub
Unverified
parent
aa46b5cf99
commit
b2424cb635
@@ -110,6 +110,7 @@ pub use rollout::SessionMeta;
|
||||
pub use rollout::find_archived_thread_path_by_id_str;
|
||||
#[deprecated(note = "use find_thread_path_by_id_str")]
|
||||
pub use rollout::find_conversation_path_by_id_str;
|
||||
pub use rollout::find_thread_name_by_id;
|
||||
pub use rollout::find_thread_path_by_id_str;
|
||||
pub use rollout::find_thread_path_by_name_str;
|
||||
pub use rollout::list::Cursor;
|
||||
|
||||
@@ -24,6 +24,7 @@ pub use list::find_thread_path_by_id_str as find_conversation_path_by_id_str;
|
||||
pub use list::rollout_date_parts;
|
||||
pub use recorder::RolloutRecorder;
|
||||
pub use recorder::RolloutRecorderParams;
|
||||
pub use session_index::find_thread_name_by_id;
|
||||
pub use session_index::find_thread_path_by_name_str;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1409,6 +1409,8 @@ impl App {
|
||||
self.chat_widget.thread_id(),
|
||||
self.chat_widget.thread_name(),
|
||||
);
|
||||
self.chat_widget
|
||||
.add_plain_history_lines(vec!["/fork".magenta().into()]);
|
||||
if let Some(path) = self.chat_widget.rollout_path() {
|
||||
match self
|
||||
.server
|
||||
|
||||
@@ -37,6 +37,7 @@ use codex_core::config::ConstraintResult;
|
||||
use codex_core::config::types::Notifications;
|
||||
use codex_core::features::FEATURES;
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::find_thread_name_by_id;
|
||||
use codex_core::git_info::current_branch_name;
|
||||
use codex_core::git_info::local_git_branches;
|
||||
use codex_core::models_manager::manager::ModelsManager;
|
||||
@@ -808,6 +809,7 @@ impl ChatWidget {
|
||||
self.forked_from = event.forked_from_id;
|
||||
self.current_rollout_path = event.rollout_path.clone();
|
||||
let initial_messages = event.initial_messages.clone();
|
||||
let forked_from_id = event.forked_from_id;
|
||||
let model_for_header = event.model.clone();
|
||||
self.session_header.set_model(&model_for_header);
|
||||
self.current_collaboration_mode = self.current_collaboration_mode.with_updates(
|
||||
@@ -843,11 +845,58 @@ impl ChatWidget {
|
||||
if let Some(user_message) = self.initial_user_message.take() {
|
||||
self.submit_user_message(user_message);
|
||||
}
|
||||
if let Some(forked_from_id) = forked_from_id {
|
||||
self.emit_forked_thread_event(forked_from_id);
|
||||
}
|
||||
if !self.suppress_session_configured_redraw {
|
||||
self.request_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_forked_thread_event(&self, forked_from_id: ThreadId) {
|
||||
let app_event_tx = self.app_event_tx.clone();
|
||||
let codex_home = self.config.codex_home.clone();
|
||||
tokio::spawn(async move {
|
||||
let forked_from_id_text = forked_from_id.to_string();
|
||||
let send_name_and_id = |name: String| {
|
||||
let line: Line<'static> = vec![
|
||||
"• ".dim(),
|
||||
"Thread forked from ".into(),
|
||||
name.cyan(),
|
||||
" (".into(),
|
||||
forked_from_id_text.clone().cyan(),
|
||||
")".into(),
|
||||
]
|
||||
.into();
|
||||
app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
PlainHistoryCell::new(vec![line]),
|
||||
)));
|
||||
};
|
||||
let send_id_only = || {
|
||||
let line: Line<'static> = vec![
|
||||
"• ".dim(),
|
||||
"Thread forked from ".into(),
|
||||
forked_from_id_text.clone().cyan(),
|
||||
]
|
||||
.into();
|
||||
app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
PlainHistoryCell::new(vec![line]),
|
||||
)));
|
||||
};
|
||||
|
||||
match find_thread_name_by_id(&codex_home, &forked_from_id).await {
|
||||
Ok(Some(name)) if !name.trim().is_empty() => {
|
||||
send_name_and_id(name);
|
||||
}
|
||||
Ok(_) => send_id_only(),
|
||||
Err(err) => {
|
||||
tracing::warn!("Failed to read forked thread name: {err}");
|
||||
send_id_only();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn on_thread_name_updated(&mut self, event: codex_core::protocol::ThreadNameUpdatedEvent) {
|
||||
if self.thread_id == Some(event.thread_id) {
|
||||
self.thread_name = event.thread_name;
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
expression: combined
|
||||
---
|
||||
• Thread forked from named-thread (e9f18a88-8081-4e51-9d4e-8af5cde2d8dd)
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests.rs
|
||||
expression: combined
|
||||
---
|
||||
• Thread forked from 019c2d47-4935-7423-a190-05691f566092
|
||||
@@ -246,6 +246,70 @@ async fn replayed_user_message_preserves_text_elements_and_local_images() {
|
||||
assert_eq!(stored_images, local_images);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn forked_thread_history_line_includes_name_and_id_snapshot() {
|
||||
let (chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
let mut chat = chat;
|
||||
let temp = tempdir().expect("tempdir");
|
||||
chat.config.codex_home = temp.path().to_path_buf();
|
||||
|
||||
let forked_from_id =
|
||||
ThreadId::from_string("e9f18a88-8081-4e51-9d4e-8af5cde2d8dd").expect("forked id");
|
||||
let session_index_entry = format!(
|
||||
"{{\"id\":\"{forked_from_id}\",\"thread_name\":\"named-thread\",\"updated_at\":\"2024-01-02T00:00:00Z\"}}\n"
|
||||
);
|
||||
std::fs::write(temp.path().join("session_index.jsonl"), session_index_entry)
|
||||
.expect("write session index");
|
||||
|
||||
chat.emit_forked_thread_event(forked_from_id);
|
||||
|
||||
let history_cell = tokio::time::timeout(std::time::Duration::from_secs(2), async {
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Some(AppEvent::InsertHistoryCell(cell)) => break cell,
|
||||
Some(_) => continue,
|
||||
None => panic!("app event channel closed before forked thread history was emitted"),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("timed out waiting for forked thread history");
|
||||
let combined = lines_to_single_string(&history_cell.display_lines(80));
|
||||
|
||||
assert!(
|
||||
combined.contains("Thread forked from"),
|
||||
"expected forked thread message in history"
|
||||
);
|
||||
assert_snapshot!("forked_thread_history_line", combined);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn forked_thread_history_line_without_name_shows_id_once_snapshot() {
|
||||
let (chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
let mut chat = chat;
|
||||
let temp = tempdir().expect("tempdir");
|
||||
chat.config.codex_home = temp.path().to_path_buf();
|
||||
|
||||
let forked_from_id =
|
||||
ThreadId::from_string("019c2d47-4935-7423-a190-05691f566092").expect("forked id");
|
||||
chat.emit_forked_thread_event(forked_from_id);
|
||||
|
||||
let history_cell = tokio::time::timeout(std::time::Duration::from_secs(2), async {
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Some(AppEvent::InsertHistoryCell(cell)) => break cell,
|
||||
Some(_) => continue,
|
||||
None => panic!("app event channel closed before forked thread history was emitted"),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("timed out waiting for forked thread history");
|
||||
let combined = lines_to_single_string(&history_cell.display_lines(80));
|
||||
|
||||
assert_snapshot!("forked_thread_history_line_without_name", combined);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn submission_preserves_text_elements_and_local_images() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
Reference in New Issue
Block a user