From 8a6cb81d3a5cfd22859ac7c95a7085fcb6b42965 Mon Sep 17 00:00:00 2001 From: Miguel Palhas Date: Mon, 24 Aug 2026 15:50:28 +0100 Subject: [PATCH] feat(maestro): Enter submits the TUI draft pane from any field (#97) DirPicker mirrors the web filter/picker split: typing narrows and the next Enter submits with the highlighted project directly (nothing was chosen yet); an explicit Up/Down move onto a different row is mid-selection, so Enter there only chooses it and a further Enter (now the active pick) submits. WorktreeChoice, BranchExisting, Preset and BranchMode already apply their highlight eagerly on Up/Down/Left/Right, so Enter always submits there. Advanced and WorktreeToggle keep activating on Enter, matching the web form's own button controls. --- crates/maestro-tui/src/draft.rs | 177 ++++++++++++++++++++++++++++---- 1 file changed, 157 insertions(+), 20 deletions(-) diff --git a/crates/maestro-tui/src/draft.rs b/crates/maestro-tui/src/draft.rs index 9a4c360..cbc074c 100644 --- a/crates/maestro-tui/src/draft.rs +++ b/crates/maestro-tui/src/draft.rs @@ -199,6 +199,12 @@ pub struct DraftPane { /// Highlighted entry within the visible step-one list; the implicit /// `Other directory…` entry sits at index `visible_groups().len()`. pub highlighted: usize, + /// Set by an explicit Up/Down move, cleared by typing (#97): typing + /// mirrors the web form's plain filter input, where Enter always + /// resolves and submits; an explicit arrow move mirrors Tab landing on + /// a not-yet-active picker button there, where a first Enter only + /// chooses. See `dir_picker_choice_active` at the Enter match arm. + dir_picker_navigated: bool, pub cwd_raw_visible: bool, pub cwd_raw: crate::text_input::TextInput, pub picked_cwd: Option, @@ -270,6 +276,7 @@ impl Default for DraftPane { groups: Vec::new(), filter: crate::text_input::TextInput::default(), highlighted: 0, + dir_picker_navigated: false, cwd_raw_visible: false, cwd_raw: crate::text_input::TextInput::default(), picked_cwd: None, @@ -769,6 +776,7 @@ impl DraftPane { if count == 0 { return; } + self.dir_picker_navigated = true; self.highlighted = ((self.selection() as i32) + delta).rem_euclid(count) as usize; } @@ -804,6 +812,17 @@ impl DraftPane { rows } + /// Whether the highlighted step-one row is already the picked group + /// (#97): once true, a further Enter submits instead of re-picking it. + /// Always false on the trailing "Other directory…" row — picking it is + /// never a no-op, so it always takes the Enter to reveal the raw field. + fn dir_picker_choice_active(&self) -> bool { + let visible = self.visible_groups(); + let sel = self.selection(); + sel < visible.len() + && self.selected_group_key.as_ref() == Some(&group_key(&visible[sel].main)) + } + /// Act on the highlighted step-one box: reveals step two, defaulting per /// rule 4. The trailing entry reveals the free-text path input and /// clears both steps (rule 3). @@ -933,27 +952,33 @@ impl DraftPane { self.prev_field(); DraftInputResult::Consumed } - InputKey::Enter => match self.focused_field { - DraftField::Submit => DraftInputResult::Submit, - DraftField::DirPicker => { - self.select_entry(); + // Enter submits from any field (#97), mirroring web/src/spawnForm.ts. + // Two fields keep their own Enter instead: `DirPicker`, whose + // highlighted row isn't the picked one until a first Enter chooses + // it (the next Enter, now that it's active, falls through to + // submit); and the disclosure/toggle pair (`Advanced`, + // `WorktreeToggle`), which activate on Enter like a button rather + // than submitting, same as the web form's own `