2 Commits

Author SHA1 Message Date
Uber Veng
cb85dfa039 v1.0.3 vim motions 2026-05-22 00:10:31 +07:00
Uber Veng
9600e49385 added vim motions 2026-05-22 00:06:30 +07:00
3 changed files with 74 additions and 9 deletions

2
Cargo.lock generated
View File

@@ -1123,7 +1123,7 @@ dependencies = [
[[package]] [[package]]
name = "ostiary" name = "ostiary"
version = "1.0.1" version = "1.0.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"crossterm", "crossterm",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "ostiary" name = "ostiary"
version = "1.0.1" version = "1.0.3"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

@@ -297,6 +297,11 @@ impl App {
Some(executor::StructuredCommand::Confirm { .. }) Some(executor::StructuredCommand::Confirm { .. })
); );
let has_command = current_command.is_some(); let has_command = current_command.is_some();
// Vim motions are disabled only when free text input is active.
let is_text_input = matches!(
current_command,
Some(executor::StructuredCommand::Input { .. })
);
match key.code { match key.code {
// ── Log scrolling (PageUp / PageDown always work) ─ // ── Log scrolling (PageUp / PageDown always work) ─
@@ -394,7 +399,66 @@ impl App {
let _ = current_command.take(); let _ = current_command.take();
let _ = reply_tx.send("n".to_string()); let _ = reply_tx.send("n".to_string());
} else { } else {
// Kill the script if still running. if let Some(kx) = kill_tx.take() {
let _ = kx.send(());
}
self.popup = None;
}
}
// ── Vim motions (off during text input) ───────────
KeyCode::Char('k') if !is_text_input => {
if is_menu {
if *menu_selected_index > 0 {
*menu_selected_index -= 1;
}
} else if !has_command {
*log_scroll_pos = log_scroll_pos.saturating_sub(1);
*log_follow_bottom = false;
}
}
KeyCode::Char('j') if !is_text_input => {
if is_menu {
if let Some(executor::StructuredCommand::Menu {
options, ..
}) = current_command
{
if *menu_selected_index + 1 < options.len() {
*menu_selected_index += 1;
}
}
} else if !has_command {
*log_scroll_pos = log_scroll_pos.saturating_add(1);
*log_follow_bottom =
*log_scroll_pos + 1 >= log_buffer.len();
}
}
KeyCode::Char('l') if !is_text_input => {
// Forward / confirm — same logic as Enter.
if let Some(cmd) = current_command.take() {
let response = match &cmd {
executor::StructuredCommand::Confirm { .. } => {
"y".to_string()
}
executor::StructuredCommand::Menu {
options, ..
} => options
.get(*menu_selected_index)
.map(|o| o.id.clone())
.unwrap_or_default(),
_ => String::new(),
};
let _ = reply_tx.send(response);
input_buffer.clear();
*menu_selected_index = 0;
}
}
KeyCode::Char('h') if !is_text_input => {
// Back / cancel — same logic as Esc.
if is_confirm {
let _ = current_command.take();
let _ = reply_tx.send("n".to_string());
} else {
if let Some(kx) = kill_tx.take() { if let Some(kx) = kill_tx.take() {
let _ = kx.send(()); let _ = kx.send(());
} }
@@ -414,7 +478,7 @@ impl App {
Popup::UpdateConfirm { info } => { Popup::UpdateConfirm { info } => {
match key.code { match key.code {
KeyCode::Char('y') | KeyCode::Char('Y') => { KeyCode::Char('y') | KeyCode::Char('Y') | KeyCode::Char('l') => {
let info = info.clone(); let info = info.clone();
self.popup = Some(Popup::Updating { self.popup = Some(Popup::Updating {
info: info.clone(), info: info.clone(),
@@ -453,7 +517,8 @@ impl App {
} }
}); });
} }
KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => { KeyCode::Char('n') | KeyCode::Char('N')
| KeyCode::Esc | KeyCode::Char('h') => {
self.popup = None; self.popup = None;
} }
_ => {} _ => {}
@@ -478,24 +543,24 @@ impl App {
// ── Main menu navigation ───────────────────────────────────────── // ── Main menu navigation ─────────────────────────────────────────
match key.code { match key.code {
KeyCode::Char('q') => return Ok(true), KeyCode::Char('q') => return Ok(true),
KeyCode::Up => { KeyCode::Up | KeyCode::Char('k') => {
let len = self.current_items().len(); let len = self.current_items().len();
if len > 0 { if len > 0 {
self.selected_index = (self.selected_index + len - 1) % len; self.selected_index = (self.selected_index + len - 1) % len;
} }
} }
KeyCode::Down => { KeyCode::Down | KeyCode::Char('j') => {
let len = self.current_items().len(); let len = self.current_items().len();
if len > 0 { if len > 0 {
self.selected_index = (self.selected_index + 1) % len; self.selected_index = (self.selected_index + 1) % len;
} }
} }
KeyCode::Enter => { KeyCode::Enter | KeyCode::Char('l') => {
if let Some(item) = self.selected_item().cloned() { if let Some(item) = self.selected_item().cloned() {
self.activate_item(item).await?; self.activate_item(item).await?;
} }
} }
KeyCode::Esc => { KeyCode::Esc | KeyCode::Char('h') => {
if !self.breadcrumbs.is_empty() { if !self.breadcrumbs.is_empty() {
self.breadcrumbs.pop(); self.breadcrumbs.pop();
self.selected_index = 0; self.selected_index = 0;