Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["core", "cli"]
resolver = "2"

[workspace.package]
version = "0.0.2"
version = "0.0.3"
edition = "2021"
authors = ["Blushyes"]
license = "MIT OR Apache-2.0"
Expand Down
28 changes: 24 additions & 4 deletions cli/src/interactive/components/input_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub struct EnhancedTextInputProps {
pub on_change: Handler<'static, String>,
pub on_submit: Handler<'static, String>,
pub on_cursor_position_change: Handler<'static, (usize, usize)>, // (line, column)
pub on_file_list_state_change: Handler<'static, bool>, // Track file list visibility
pub width: u16,
pub placeholder: String,
pub color: Option<Color>,
Expand All @@ -156,6 +157,7 @@ impl Default for EnhancedTextInputProps {
on_change: Handler::default(),
on_submit: Handler::default(),
on_cursor_position_change: Handler::default(),
on_file_list_state_change: Handler::default(),
width: 80,
placeholder: String::new(),
color: None,
Expand Down Expand Up @@ -199,6 +201,7 @@ pub fn EnhancedTextInput(
let mut on_change = props.on_change.take();
let mut on_submit = props.on_submit.take();
let mut on_cursor_position_change = props.on_cursor_position_change.take();
let mut on_file_list_state_change = props.on_file_list_state_change.take();
let mut value = props.value.clone();
let mut cursor_pos = cursor_pos;
let mut show_file_list = show_file_list;
Expand Down Expand Up @@ -269,6 +272,7 @@ pub fn EnhancedTextInput(
if recent_text && matches!(code, KeyCode::Enter) {
// Close popup and let the general handler below process Enter as newline
show_file_list.set(false);
on_file_list_state_change(false);
// fallthrough to general handling by not returning
} else {
// Insert selected file
Expand Down Expand Up @@ -304,11 +308,13 @@ pub fn EnhancedTextInput(
}
}
show_file_list.set(false);
on_file_list_state_change(false);
return;
}
}
KeyCode::Esc => {
show_file_list.set(false);
on_file_list_state_change(false);
return;
}
_ => {}
Expand Down Expand Up @@ -375,12 +381,14 @@ pub fn EnhancedTextInput(
selected_file_index.set(0);
current_query.set(query);
show_file_list.set(true);
on_file_list_state_change(true);
}
}
}
} else {
// Should not show list, hide it
show_file_list.set(false);
on_file_list_state_change(false);
}
}
KeyCode::Backspace => {
Expand Down Expand Up @@ -451,12 +459,14 @@ pub fn EnhancedTextInput(
selected_file_index.set(0);
current_query.set(query);
show_file_list.set(true);
on_file_list_state_change(true);
}
}
}
} else {
// Should not show list, hide it
show_file_list.set(false);
on_file_list_state_change(false);
}
}
}
Expand Down Expand Up @@ -524,6 +534,7 @@ pub fn EnhancedTextInput(
} else {
// No valid query found, hide the list
show_file_list.set(false);
on_file_list_state_change(false);
}
}
}
Expand Down Expand Up @@ -1083,6 +1094,9 @@ pub fn InputSection(mut hooks: Hooks, props: &InputSectionProps) -> impl Into<An
let project_path = context.project_path.clone();
let ui_sender = context.ui_sender.clone();

// State to track file list visibility for history navigation
let file_list_visible = hooks.use_state(|| false);

// Handle keyboard events for task interruption and history navigation
hooks.use_terminal_events({
let ui_sender = ui_sender.clone();
Expand All @@ -1105,8 +1119,8 @@ pub fn InputSection(mut hooks: Hooks, props: &InputSectionProps) -> impl Into<An
}
}
KeyCode::Up => {
// Navigate to previous history entry
if !*is_task_running.read() {
// Navigate to previous history entry only if file list is not showing
if !*is_task_running.read() && !*file_list_visible.read() {
let current_input = input_value.read().clone();
if let Some(history_text) =
input_history.write().navigate_previous(&current_input)
Expand All @@ -1117,8 +1131,8 @@ pub fn InputSection(mut hooks: Hooks, props: &InputSectionProps) -> impl Into<An
}
}
KeyCode::Down => {
// Navigate to next history entry
if !*is_task_running.read() {
// Navigate to next history entry only if file list is not showing
if !*is_task_running.read() && !*file_list_visible.read() {
if let Some(history_text) = input_history.write().navigate_next() {
input_value.set(history_text.clone());
cursor_position.set((1, history_text.len() + 1));
Expand Down Expand Up @@ -1163,6 +1177,12 @@ pub fn InputSection(mut hooks: Hooks, props: &InputSectionProps) -> impl Into<An
cursor_position.set((line, col));
}
},
on_file_list_state_change: {
let mut file_list_visible = file_list_visible;
move |is_visible| {
file_list_visible.set(is_visible);
}
},
on_submit: {
let mut input_value = input_value;
let mut cursor_position = cursor_position;
Expand Down
10 changes: 5 additions & 5 deletions cli/src/interactive/input_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,14 +550,14 @@ mod tests {
assert_eq!(text_history_load.entries[0].text, test_commands[999]); // Most recent
assert_eq!(text_history_load.entries[999].text, test_commands[0]); // Oldest

// Text format should be very fast (under 10ms for 1000 entries)
// Text format should be very fast (under 50ms for 1000 entries)
assert!(
text_save_duration.as_millis() < 10,
"Text save should be under 10ms"
text_save_duration.as_millis() < 50,
"Text save should be under 50ms"
);
assert!(
text_load_duration.as_millis() < 10,
"Text load should be under 10ms"
text_load_duration.as_millis() < 50,
"Text load should be under 50ms"
);
}
}
9 changes: 1 addition & 8 deletions cli/src/interactive/task_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,14 @@ pub async fn execute_agent_task_with_context(
Ok(())
};

// Add timeout to prevent hanging
let timeout_future = tokio::time::sleep(tokio::time::Duration::from_secs(300)); // 5 minutes timeout

// Race between task execution, interruption, and timeout
// Race between task execution and interruption
tokio::select! {
result = task_future => {
result?;
}
interrupt_result = interrupt_future => {
interrupt_result?;
}
_ = timeout_future => {
tracing::error!("Task execution timed out after 5 minutes");
return Err(anyhow::anyhow!("Task execution timed out"));
}
}

Ok(())
Expand Down