diff --git a/Cargo.lock b/Cargo.lock index e9197c63..ad3cdce2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5490,7 +5490,7 @@ dependencies = [ [[package]] name = "stakpak-popup-widget" -version = "0.3.1" +version = "0.3.4" dependencies = [ "crossterm 0.29.0", "ratatui", diff --git a/libs/api/src/local/context_managers/file_scratchpad_context_manager.rs b/libs/api/src/local/context_managers/file_scratchpad_context_manager.rs index d7de187f..8e3e7bed 100644 --- a/libs/api/src/local/context_managers/file_scratchpad_context_manager.rs +++ b/libs/api/src/local/context_managers/file_scratchpad_context_manager.rs @@ -73,17 +73,17 @@ impl FileScratchpadContextManager { } fn load_file(&self, path: &Path) -> String { - fs::read_to_string(&path).unwrap_or_default() + fs::read_to_string(path).unwrap_or_default() } fn resolve_path(&self, base_path: &Path, session_id: Option) -> PathBuf { - if let Some(session_id) = session_id { - if let Some(parent) = base_path.parent() { - // If there's a session ID, put files in a subdirectory named after the session ID - // e.g. .stakpak/session/scratchpad.md -> .stakpak/session//scratchpad.md - let session_dir = parent.join(session_id.to_string()); - return session_dir.join(base_path.file_name().unwrap_or_default()); - } + if let Some(session_id) = session_id + && let Some(parent) = base_path.parent() + { + // If there's a session ID, put files in a subdirectory named after the session ID + // e.g. .stakpak/session/scratchpad.md -> .stakpak/session//scratchpad.md + let session_dir = parent.join(session_id.to_string()); + return session_dir.join(base_path.file_name().unwrap_or_default()); } base_path.to_path_buf() } @@ -197,17 +197,16 @@ impl FileScratchpadContextManager { if let (Some(old_str), Some(new_str)) = ( args.get("old_str").and_then(|s| s.as_str()), args.get("new_str").and_then(|s| s.as_str()), - ) { - if let Some(content) = current_content { - let replace_all = args - .get("replace_all") - .and_then(|b| b.as_bool()) - .unwrap_or(false); - if replace_all { - *content = content.replace(old_str, new_str); - } else { - *content = content.replacen(old_str, new_str, 1); - } + ) && let Some(content) = current_content + { + let replace_all = args + .get("replace_all") + .and_then(|b| b.as_bool()) + .unwrap_or(false); + if replace_all { + *content = content.replace(old_str, new_str); + } else { + *content = content.replacen(old_str, new_str, 1); } } } diff --git a/tui/src/event.rs b/tui/src/event.rs index bd45e389..ba5059a3 100644 --- a/tui/src/event.rs +++ b/tui/src/event.rs @@ -96,7 +96,13 @@ pub fn map_crossterm_event_to_input_event(event: Event) -> Option { Some(InputEvent::InputBackspace) } } - KeyCode::Enter => Some(InputEvent::InputSubmitted), + KeyCode::Enter => { + if key.modifiers.contains(KeyModifiers::SHIFT) { + Some(InputEvent::InputChangedNewline) + } else { + Some(InputEvent::InputSubmitted) + } + } KeyCode::Esc => Some(InputEvent::HandleEsc), KeyCode::Up => Some(InputEvent::Up), KeyCode::Down => Some(InputEvent::Down), diff --git a/tui/src/event_loop.rs b/tui/src/event_loop.rs index 7f43ed7c..62eee006 100644 --- a/tui/src/event_loop.rs +++ b/tui/src/event_loop.rs @@ -12,6 +12,7 @@ use crate::services::message::Message; use crate::view::view; use crossterm::event::{ DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, + KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, }; use crossterm::{execute, terminal::EnterAlternateScreen}; use ratatui::{Terminal, backend::CrosstermBackend}; @@ -61,7 +62,17 @@ pub async fn run_tui( execute!( std::io::stdout(), EnterAlternateScreen, - EnableBracketedPaste + EnableBracketedPaste, + )?; + + #[cfg(unix)] + execute!( + std::io::stdout(), + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_EVENT_TYPES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + ) )?; #[cfg(unix)] @@ -276,8 +287,11 @@ pub async fn run_tui( std::io::stdout(), crossterm::terminal::LeaveAlternateScreen, DisableBracketedPaste, - DisableMouseCapture + DisableMouseCapture, )?; + + #[cfg(unix)] + execute!(std::io::stdout(), PopKeyboardEnhancementFlags)?; Ok(()) } diff --git a/tui/src/services/hint_helper.rs b/tui/src/services/hint_helper.rs index 15071209..88e15d3d 100644 --- a/tui/src/services/hint_helper.rs +++ b/tui/src/services/hint_helper.rs @@ -185,8 +185,13 @@ pub fn render_hint_or_shortcuts(f: &mut Frame, state: &AppState, area: Rect) { // detect if terminal is vscode let terminal_info = detect_terminal(); let terminal_name = terminal_info.emulator; - let is_iterm2 = terminal_name == "iTerm2"; - let new_line_hint = if !is_iterm2 { "ctrl+j" } else { "shift+enter" }; + let is_terminal_app_or_vscode = + terminal_name == "VS Code Terminal" || terminal_name == "Terminal.app"; + let new_line_hint = if is_terminal_app_or_vscode { + "ctrl+j" + } else { + "shift+enter" + }; let hint = Paragraph::new(Span::styled( format!( "{} new line | {} | ctrl+o toggle auto-approve",