aboutsummaryrefslogtreecommitdiff
path: root/src/app/mod.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-10 02:26:25 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-10 02:27:41 +0300
commit77fcde0a2736c3927d1bc589bd9b4d0173a9df46 (patch)
tree209c704342fb52a7ecbb5fe0cdbc291f7650e720 /src/app/mod.rs
parent0facd819b4747c7634040b561509137c2608b024 (diff)
fix: support paste in modal inputs
refs #302
Diffstat (limited to 'src/app/mod.rs')
-rw-r--r--src/app/mod.rs79
1 files changed, 76 insertions, 3 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 936b082f..35ab92b1 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -1326,7 +1326,7 @@ impl App {
self.handle_terminal_key_headless(key);
} else {
self.suppressed_repeat_keys.insert(key_id);
- self.handle_non_terminal_key(key);
+ self.handle_non_terminal_key_headless(key);
}
}
crossterm::event::KeyEventKind::Repeat => {
@@ -1352,7 +1352,9 @@ impl App {
}
}
crate::raw_input::RawInputEvent::Paste(text) => {
- if self.state.mode == Mode::Terminal {
+ if self.state.mode != Mode::Terminal {
+ self.paste_into_active_text_input(&text);
+ } else {
if let Some(ws_idx) = self.state.active {
if let Some(ws) = self.state.workspaces.get(ws_idx) {
if let Some(focused) = ws.focused_pane_id() {
@@ -1395,8 +1397,17 @@ impl App {
///
/// Uses the standalone handler functions that work on `&mut AppState`
/// since the server doesn't have the async context of the monolithic App.
- fn handle_non_terminal_key(&mut self, key: crate::input::TerminalKey) {
+ fn handle_non_terminal_key_headless(&mut self, key: crate::input::TerminalKey) {
let key_event = key.as_key_event();
+ if input::modal_paste_target_active(&self.state)
+ && input::is_modal_paste_shortcut(&key_event)
+ {
+ if let Some(text) = crate::platform::read_clipboard_text() {
+ self.paste_into_active_text_input(&text);
+ }
+ return;
+ }
+
match self.state.mode {
Mode::Prefix => {
self.handle_prefix_key(key);
@@ -3969,6 +3980,68 @@ last_pane = "prefix+tab"
}
#[test]
+ fn route_client_input_pastes_bracketed_text_into_rename_modal() {
+ let mut app = test_app();
+ app.state.workspaces = vec![Workspace::test_new("test")];
+ app.state.active = Some(0);
+ app.state.selected = 0;
+ app.state.mode = Mode::RenameTab;
+ app.state.name_input = "2".into();
+ app.state.name_input_replace_on_type = true;
+
+ app.route_client_input(b"\x1b[200~feature/logs\x1b[201~".to_vec());
+
+ assert_eq!(app.state.name_input, "feature/logs");
+ assert!(!app.state.name_input_replace_on_type);
+ }
+
+ #[test]
+ fn raw_ctrl_v_decodes_as_modal_paste_shortcut() {
+ let events = crate::raw_input::parse_raw_input_bytes_sync(&[0x16]);
+ let Some(crate::raw_input::RawInputEvent::Key(key)) = events.first() else {
+ panic!("expected ctrl-v key event");
+ };
+
+ assert!(input::is_modal_paste_shortcut(&key.as_key_event()));
+ }
+
+ #[test]
+ fn route_client_events_pastes_text_into_new_linked_worktree_modal() {
+ let mut app = test_app();
+ app.state.mode = Mode::NewLinkedWorktree;
+ app.state.name_input = "generated-branch".into();
+ app.state.name_input_replace_on_type = true;
+ app.state.worktree_create = Some(state::WorktreeCreateState {
+ source_workspace_id: "source".into(),
+ source_checkout_path: "/repo/herdr".into(),
+ source_existing_membership: None,
+ source_repo_root: "/repo/herdr".into(),
+ repo_key: "repo-key".into(),
+ repo_name: "herdr".into(),
+ branch: "generated-branch".into(),
+ checkout_path: "/repo/herdr-generated-branch".into(),
+ error: None,
+ creating: false,
+ });
+
+ app.route_client_events(
+ vec![crate::raw_input::RawInputEvent::Paste(
+ "feature/linear-302".into(),
+ )],
+ true,
+ );
+
+ assert_eq!(app.state.name_input, "feature/linear-302");
+ assert_eq!(
+ app.state
+ .worktree_create
+ .as_ref()
+ .map(|create| create.branch.as_str()),
+ Some("feature/linear-302")
+ );
+ }
+
+ #[test]
fn route_client_input_closes_release_notes_modal() {
let mut app = test_app();
app.state.workspaces = vec![Workspace::test_new("test")];