use super::super::*;
use super::support::*;
use std::time::Instant;
#[test]
fn double_ctrl_accepts_crossterm_standalone_left_and_right_ctrl_modifier_keys() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let left_ctrl = modified_key(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::CONTROL,
);
let right_ctrl = modified_key(
KeyCode::Modifier(ModifierKeyCode::RightControl),
KeyModifiers::CONTROL,
);
assert_eq!(
handle_key_at(left_ctrl, &mut state, KeyViewports::default(), &[], first),
InputAction::None
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
assert_eq!(
handle_key_at(
right_ctrl,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(250),
),
InputAction::None
);
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn double_ctrl_accepts_modifier_ctrl_keys_without_ctrl_modifier_flag() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let left_ctrl = key(KeyCode::Modifier(ModifierKeyCode::LeftControl));
let right_ctrl = key(KeyCode::Modifier(ModifierKeyCode::RightControl));
assert_eq!(
handle_key_at(left_ctrl, &mut state, KeyViewports::default(), &[], first),
InputAction::None
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
assert_eq!(
handle_key_at(
right_ctrl,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(250),
),
InputAction::None
);
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn standalone_alt_no_longer_arms_prompt_focus_shortcut() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let standalone_alt = modified_key(
KeyCode::Modifier(ModifierKeyCode::LeftAlt),
KeyModifiers::ALT,
);
assert_eq!(
handle_key_at(
standalone_alt,
&mut state,
KeyViewports::default(),
&[],
first,
),
InputAction::None
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_none());
assert_eq!(
handle_key_at(
standalone_alt,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(250),
),
InputAction::None
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn plain_escape_does_not_arm_double_ctrl() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
assert_eq!(
handle_key_at(
key(KeyCode::Esc),
&mut state,
KeyViewports::default(),
&[],
first,
),
InputAction::None
);
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn double_ctrl_focuses_prompt_within_timing_window() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let standalone_ctrl = modified_key(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::CONTROL,
);
assert_eq!(
handle_key_at(
standalone_ctrl,
&mut state,
KeyViewports::default(),
&[],
first
),
InputAction::None
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
assert_eq!(
handle_key_at(
standalone_ctrl,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(250),
),
InputAction::None
);
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
assert_eq!(
handle_key_at(
key(KeyCode::Char('x')),
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(300),
),
InputAction::None
);
assert_eq!(state.prompt_plain_text(), "x");
}
#[test]
fn double_ctrl_accepts_release_only_modifier_ctrl_events() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let standalone_ctrl_release = modified_key_with_kind(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::NONE,
crossterm::event::KeyEventKind::Release,
);
assert_eq!(
handle_key_at(
standalone_ctrl_release,
&mut state,
KeyViewports::default(),
&[],
first,
),
InputAction::None
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
assert_eq!(
handle_key_at(
standalone_ctrl_release,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(250),
),
InputAction::None
);
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn single_ctrl_press_release_does_not_focus_prompt() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let ctrl_press = modified_key_with_kind(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::CONTROL,
crossterm::event::KeyEventKind::Press,
);
let ctrl_release = modified_key_with_kind(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::NONE,
crossterm::event::KeyEventKind::Release,
);
handle_key_at(ctrl_press, &mut state, KeyViewports::default(), &[], first);
handle_key_at(
ctrl_release,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(20),
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
}
#[test]
fn ctrl_shortcut_release_does_not_arm_double_ctrl() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("abc", 0),
..Default::default()
};
let first = Instant::now();
let ctrl_c = modified_key_with_kind(
KeyCode::Char('c'),
KeyModifiers::CONTROL,
crossterm::event::KeyEventKind::Press,
);
let ctrl_release = modified_key_with_kind(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::NONE,
crossterm::event::KeyEventKind::Release,
);
assert_eq!(
handle_key_at(ctrl_c, &mut state, KeyViewports::default(), &[], first),
InputAction::None
);
assert!(state.prompt_plain_text().is_empty());
assert_eq!(
handle_key_at(
ctrl_release,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(20),
),
InputAction::None
);
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn double_ctrl_focuses_prompt_with_modal_active() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
state.open_system_prompt_modal("modal content".to_string());
let first = Instant::now();
let standalone_ctrl = modified_key(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::CONTROL,
);
assert_eq!(
handle_key_at(
standalone_ctrl,
&mut state,
KeyViewports::default(),
&[],
first
),
InputAction::None
);
assert!(state.system_prompt_modal_visible());
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
assert_eq!(
handle_key_at(
standalone_ctrl,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(250),
),
InputAction::None
);
assert!(state.system_prompt_modal_visible());
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn double_ctrl_times_out_and_does_not_hijack_escape() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Transcript,
..Default::default()
};
let first = Instant::now();
let standalone_ctrl = modified_key(
KeyCode::Modifier(ModifierKeyCode::LeftControl),
KeyModifiers::CONTROL,
);
handle_key_at(
standalone_ctrl,
&mut state,
KeyViewports::default(),
&[],
first,
);
handle_key_at(
standalone_ctrl,
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(600),
);
assert!(state.is_transcript_focused());
assert!(state.last_standalone_ctrl.is_some());
handle_key_at(
key(KeyCode::Esc),
&mut state,
KeyViewports::default(),
&[],
first + Duration::from_millis(650),
);
assert!(state.is_prompt_focused());
assert!(state.last_standalone_ctrl.is_none());
}