feat: Tambah Ctrl+Y untuk menyalin pesan assistant terakhir
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3b6abd920e
commit
d93bdba59b
@@ -132,6 +132,23 @@ pub fn handle_key(key: KeyEvent, state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
KeyCode::Char('d') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
vec![Action::CloseOverlay]
|
vec![Action::CloseOverlay]
|
||||||
}
|
}
|
||||||
|
KeyCode::Char('y') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
let last_assistant = state.transcript_cache.messages.iter()
|
||||||
|
.rev()
|
||||||
|
.find(|m| m.role == crate::dto::chat::message::Role::Assistant);
|
||||||
|
match last_assistant {
|
||||||
|
Some(msg) => {
|
||||||
|
state.misc.pending_clipboard_copy = Some(msg.content.clone());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
state.push_toast(crate::app::state::types::Toast::new(
|
||||||
|
crate::app::state::types::ToastKind::Info,
|
||||||
|
"No assistant message to copy yet".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
if state.input.autocomplete_visible {
|
if state.input.autocomplete_visible {
|
||||||
state.input.select_autocomplete();
|
state.input.select_autocomplete();
|
||||||
@@ -371,3 +388,45 @@ fn handle_overlay_enter(state: &mut AppStateRest) -> Vec<Action> {
|
|||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn test_state() -> AppStateRest {
|
||||||
|
let tmp = std::env::temp_dir().join(format!("zesdex-input-test-{}", uuid::Uuid::new_v4()));
|
||||||
|
std::fs::create_dir_all(&tmp).unwrap();
|
||||||
|
AppStateRest::new(vec![tmp.clone()], &tmp, tmp.join("memory"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ctrl_y_sets_pending_clipboard_copy_to_last_assistant_message() {
|
||||||
|
let mut state = test_state();
|
||||||
|
state.push_transcript(crate::app::state::rest::ChatMessageDisplay::new(
|
||||||
|
crate::dto::chat::message::Role::User,
|
||||||
|
"hi".to_string(),
|
||||||
|
));
|
||||||
|
state.push_transcript(crate::app::state::rest::ChatMessageDisplay::new(
|
||||||
|
crate::dto::chat::message::Role::Assistant,
|
||||||
|
"first reply".to_string(),
|
||||||
|
));
|
||||||
|
state.push_transcript(crate::app::state::rest::ChatMessageDisplay::new(
|
||||||
|
crate::dto::chat::message::Role::Tool,
|
||||||
|
"tool output".to_string(),
|
||||||
|
));
|
||||||
|
state.push_transcript(crate::app::state::rest::ChatMessageDisplay::new(
|
||||||
|
crate::dto::chat::message::Role::Assistant,
|
||||||
|
"second reply".to_string(),
|
||||||
|
));
|
||||||
|
handle_key(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::CONTROL), &mut state);
|
||||||
|
assert_eq!(state.misc.pending_clipboard_copy, Some("second reply".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ctrl_y_with_no_assistant_message_pushes_info_toast() {
|
||||||
|
let mut state = test_state();
|
||||||
|
handle_key(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::CONTROL), &mut state);
|
||||||
|
assert!(state.misc.pending_clipboard_copy.is_none());
|
||||||
|
assert_eq!(state.misc.toasts.len(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user