From a5727c0a1d63024e179ff881d7196db3b3a94a46 Mon Sep 17 00:00:00 2001 From: vikingowl Date: Tue, 30 Sep 2025 02:27:09 +0200 Subject: [PATCH] Enhance TUI: add paste event handling, integrate bracketed paste mode, and implement paste support in editing mode. Fixes #20 --- crates/owlen-cli/src/main.rs | 7 ++++--- crates/owlen-tui/src/chat_app.rs | 18 ++++++++++++++++++ crates/owlen-tui/src/events.rs | 5 +++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/owlen-cli/src/main.rs b/crates/owlen-cli/src/main.rs index 28d2d91..6000185 100644 --- a/crates/owlen-cli/src/main.rs +++ b/crates/owlen-cli/src/main.rs @@ -11,7 +11,7 @@ use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; use crossterm::{ - event::{DisableMouseCapture, EnableMouseCapture}, + event::{DisableMouseCapture, EnableMouseCapture, DisableBracketedPaste, EnableBracketedPaste}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; @@ -57,7 +57,7 @@ async fn main() -> Result<()> { // Terminal setup enable_raw_mode()?; let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; + execute!(stdout, EnterAlternateScreen, EnableMouseCapture, EnableBracketedPaste)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; @@ -74,7 +74,8 @@ async fn main() -> Result<()> { execute!( terminal.backend_mut(), LeaveAlternateScreen, - DisableMouseCapture + DisableMouseCapture, + DisableBracketedPaste )?; terminal.show_cursor()?; diff --git a/crates/owlen-tui/src/chat_app.rs b/crates/owlen-tui/src/chat_app.rs index 10b1cbc..45cef2e 100644 --- a/crates/owlen-tui/src/chat_app.rs +++ b/crates/owlen-tui/src/chat_app.rs @@ -381,6 +381,24 @@ impl ChatApp { Event::Tick => { // Future: update streaming timers } + Event::Paste(text) => { + // Handle paste events - insert text directly without triggering sends + if matches!(self.mode, InputMode::Editing) { + // In editing mode, insert the pasted text directly into textarea + let lines: Vec<&str> = text.lines().collect(); + for (i, line) in lines.iter().enumerate() { + for ch in line.chars() { + self.textarea.insert_char(ch); + } + // Insert newline between lines (but not after the last line) + if i < lines.len() - 1 { + self.textarea.insert_newline(); + } + } + self.sync_textarea_to_buffer(); + } + // Ignore paste events in other modes + } Event::Key(key) => match self.mode { InputMode::Normal => { // Handle multi-key sequences first diff --git a/crates/owlen-tui/src/events.rs b/crates/owlen-tui/src/events.rs index 5280a2a..dd27af4 100644 --- a/crates/owlen-tui/src/events.rs +++ b/crates/owlen-tui/src/events.rs @@ -11,6 +11,8 @@ pub enum Event { /// Terminal resize event #[allow(dead_code)] Resize(u16, u16), + /// Paste event + Paste(String), /// Tick event for regular updates Tick, } @@ -60,6 +62,9 @@ impl EventHandler { crossterm::event::Event::Resize(width, height) => { let _ = self.sender.send(Event::Resize(width, height)); } + crossterm::event::Event::Paste(text) => { + let _ = self.sender.send(Event::Paste(text)); + } _ => {} } }