mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
feat(tui2): add copy selection shortcut + UI affordance (#8462)
- Detect Ctrl+Shift+C vs VS Code Ctrl+Y and surface in footer hints - Render clickable “⧉ copy” pill near transcript selection (hidden while dragging) - Handle copy hotkey + click to copy selection - Document updated copy UX VSCode: <img width="1095" height="413" alt="image" src="https://github.com/user-attachments/assets/84be0c82-4762-4c3e-80a4-c751c078bdaa" /> Ghosty: <img width="505" height="68" alt="image" src="https://github.com/user-attachments/assets/109cc1a1-f029-4f7e-a141-4c6ed2da7338" />
This commit is contained in:
1 parent
277babba79
commit
414fbe0da9
9 files changed
+596
-14
No files matched your search
@@ -1,3 +1,5 @@
|
||||
use crate::key_hint;
|
||||
use crate::key_hint::KeyBinding;
|
||||
use crate::key_hint::has_ctrl_or_alt;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -121,6 +123,7 @@ pub(crate) struct ChatComposer {
|
||||
transcript_scrolled: bool,
|
||||
transcript_selection_active: bool,
|
||||
transcript_scroll_position: Option<(usize, usize)>,
|
||||
transcript_copy_selection_key: KeyBinding,
|
||||
skills: Option<Vec<SkillMetadata>>,
|
||||
dismissed_skill_popup_token: Option<String>,
|
||||
}
|
||||
@@ -172,6 +175,7 @@ impl ChatComposer {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
skills: None,
|
||||
dismissed_skill_popup_token: None,
|
||||
};
|
||||
@@ -1540,6 +1544,7 @@ impl ChatComposer {
|
||||
transcript_scrolled: self.transcript_scrolled,
|
||||
transcript_selection_active: self.transcript_selection_active,
|
||||
transcript_scroll_position: self.transcript_scroll_position,
|
||||
transcript_copy_selection_key: self.transcript_copy_selection_key,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1571,10 +1576,12 @@ impl ChatComposer {
|
||||
scrolled: bool,
|
||||
selection_active: bool,
|
||||
scroll_position: Option<(usize, usize)>,
|
||||
copy_selection_key: KeyBinding,
|
||||
) {
|
||||
self.transcript_scrolled = scrolled;
|
||||
self.transcript_selection_active = selection_active;
|
||||
self.transcript_scroll_position = scroll_position;
|
||||
self.transcript_copy_selection_key = copy_selection_key;
|
||||
}
|
||||
|
||||
fn sync_popups(&mut self) {
|
||||
|
||||
@@ -25,6 +25,7 @@ pub(crate) struct FooterProps {
|
||||
pub(crate) transcript_scrolled: bool,
|
||||
pub(crate) transcript_selection_active: bool,
|
||||
pub(crate) transcript_scroll_position: Option<(usize, usize)>,
|
||||
pub(crate) transcript_copy_selection_key: KeyBinding,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -115,7 +116,7 @@ fn footer_lines(props: FooterProps) -> Vec<Line<'static>> {
|
||||
}
|
||||
if props.transcript_selection_active {
|
||||
line.push_span(" · ".dim());
|
||||
line.push_span(key_hint::ctrl(KeyCode::Char('y')));
|
||||
line.push_span(props.transcript_copy_selection_key);
|
||||
line.push_span(" copy selection".dim());
|
||||
}
|
||||
vec![line]
|
||||
@@ -467,6 +468,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -482,6 +484,7 @@ mod tests {
|
||||
transcript_scrolled: true,
|
||||
transcript_selection_active: true,
|
||||
transcript_scroll_position: Some((3, 42)),
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -497,6 +500,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -512,6 +516,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -527,6 +532,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -542,6 +548,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -557,6 +564,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -572,6 +580,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -587,6 +596,7 @@ mod tests {
|
||||
transcript_scrolled: false,
|
||||
transcript_selection_active: false,
|
||||
transcript_scroll_position: None,
|
||||
transcript_copy_selection_key: key_hint::ctrl_shift(KeyCode::Char('c')),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -386,9 +386,14 @@ impl BottomPane {
|
||||
scrolled: bool,
|
||||
selection_active: bool,
|
||||
scroll_position: Option<(usize, usize)>,
|
||||
copy_selection_key: crate::key_hint::KeyBinding,
|
||||
) {
|
||||
self.composer
|
||||
.set_transcript_ui_state(scrolled, selection_active, scroll_position);
|
||||
self.composer.set_transcript_ui_state(
|
||||
scrolled,
|
||||
selection_active,
|
||||
scroll_position,
|
||||
copy_selection_key,
|
||||
);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user