Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c07343f
feat(tui): add Alt+C hotkey for copying last agent response
fcoury Feb 9, 2026
859abbf
feat(tui): add /copy command and harden copy behavior
fcoury Feb 10, 2026
5a8f747
fix(tui): keep copy source while new turn is running
fcoury Feb 10, 2026
012c2c8
fix(tui): fix copy source after transcript rollback
fcoury Feb 10, 2026
f0e89af
fix(tui): gate clipboard backends for Android and macOS libc usage
fcoury Feb 10, 2026
5df4697
fix(tui): harden copy source rollback and clipboard paths
fcoury Feb 10, 2026
a5877c1
fix(tui): clear quit shortcut state on Alt+C copy
fcoury Feb 10, 2026
85a6815
fix(tui): align rollback copy state with bounded history
fcoury Feb 10, 2026
2ba6538
fix(tui): align rollback copy history with completed turns
fcoury Feb 10, 2026
718977e
fix(tui): recover copy source after deep rollback
fcoury Feb 10, 2026
aaf90a0
fix(tui): harden copy history state and OSC52 write path
fcoury Feb 10, 2026
91d40a7
fix(tui): rollback copy source selection after resume
fcoury Feb 10, 2026
5604062
fix(tui): align copy-history rollback with user turns
fcoury Feb 10, 2026
009a4af
fix(tui): copy completed plan output in plan mode
fcoury Feb 10, 2026
12e4660
fix(tui): align copy-source semantics and backtrack test
fcoury Feb 10, 2026
5d74a81
fix(tui): capture review output for copy shortcuts
fcoury Feb 10, 2026
50f73c3
refactor(tui): replace copy output state with markdown history
fcoury-oai Apr 6, 2026
82b9420
fix(tui): keep linux clipboard owner alive
fcoury-oai Apr 6, 2026
e66c276
refactor(tui): remove stale rollback hook
fcoury-oai Apr 7, 2026
980cd28
fix(tui): restore wsl clipboard fallback
fcoury-oai Apr 7, 2026
3c4b49f
fix(tui): satisfy argument comment lint
fcoury-oai Apr 7, 2026
f5424c5
docs(tui): document clipboard lease member
fcoury-oai Apr 7, 2026
9bbc972
fix(tui): restore tmux osc52 passthrough
fcoury-oai Apr 7, 2026
a6aca22
fix(tui): keep copy state aligned after local prompts
fcoury-oai Apr 7, 2026
ea672a8
fix(tui): make turn completion tests base compatible
fcoury-oai Apr 7, 2026
af639da
fix(tui): satisfy turn completion argument lint
fcoury-oai Apr 7, 2026
f32ad22
docs(tui): clarify copy-source ordinal and precedence logic
fcoury-oai Apr 7, 2026
16f4567
docs(tui): add reviewer-facing comments for copy-history data flow
fcoury-oai Apr 7, 2026
628eafb
fix(tui): use ctrl-o for copy response shortcut
fcoury-oai Apr 8, 2026
bd2a9b6
feat(tui): tooltip for copy command and shortcut
fcoury-oai Apr 8, 2026
89ab3f8
fix(tui): lower copy response history cap
fcoury-oai Apr 9, 2026
699582b
refactor(tui): keep copy response cache rollback agnostic
fcoury-oai Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion codex-rs/tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5695,7 +5695,6 @@ impl App {
}
}
self.handle_backtrack_rollback_succeeded(num_turns);
self.chat_widget.handle_thread_rolled_back();
}

fn handle_thread_event_now(&mut self, event: ThreadBufferedEvent) {
Expand Down
50 changes: 50 additions & 0 deletions codex-rs/tui/src/app_backtrack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use std::sync::Arc;
use crate::app::App;
use crate::app_command::AppCommand;
use crate::app_event::AppEvent;
#[cfg(test)]
use crate::history_cell::AgentMessageCell;
use crate::history_cell::SessionInfoCell;
use crate::history_cell::UserHistoryCell;
use crate::pager_overlay::Overlay;
Expand Down Expand Up @@ -635,6 +637,34 @@ fn user_positions_iter(
.filter_map(move |(idx, cell)| (type_of(cell) == user_type).then_some(idx))
}

#[cfg(test)]
fn agent_group_count(cells: &[Arc<dyn crate::history_cell::HistoryCell>]) -> usize {
agent_group_positions_iter(cells).count()
}

#[cfg(test)]
fn agent_group_positions_iter(
cells: &[Arc<dyn crate::history_cell::HistoryCell>],
) -> impl Iterator<Item = usize> + '_ {
let session_start_type = TypeId::of::<SessionInfoCell>();
let type_of = |cell: &Arc<dyn crate::history_cell::HistoryCell>| cell.as_any().type_id();

let start = cells
.iter()
.rposition(|cell| type_of(cell) == session_start_type)
.map_or(0, |idx| idx + 1);

cells
.iter()
.enumerate()
.skip(start)
.filter_map(move |(idx, cell)| {
let is_agent = cell.as_any().downcast_ref::<AgentMessageCell>().is_some();
let is_copy_source_group = is_agent && !cell.is_stream_continuation();
is_copy_source_group.then_some(idx)
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -831,4 +861,24 @@ mod tests {
.collect();
assert_eq!(intro_text, "• intro");
}

#[test]
fn agent_group_count_ignores_context_compacted_marker() {
let cells: Vec<Arc<dyn HistoryCell>> = vec![
Arc::new(AgentMessageCell::new(
vec![Line::from("first")],
/*is_first_line*/ true,
)) as Arc<dyn HistoryCell>,
Arc::new(crate::history_cell::new_info_event(
"Context compacted".to_string(),
/*hint*/ None,
)) as Arc<dyn HistoryCell>,
Arc::new(AgentMessageCell::new(
vec![Line::from("second")],
/*is_first_line*/ true,
)) as Arc<dyn HistoryCell>,
];

assert_eq!(agent_group_count(&cells), 2);
}
}
Loading
Loading