fix(tui): input stays in box during/after turn

- mark input bar turn-in-progress on submit
- re-enable input after turn resumes
- clear input area before redraw
- clear completion popup before redraw
- resume re-enters alternate screen and clears cleanly

This prevents typed text from overwriting itself and stops redraws from
spilling word-wrapped text outside the input box.
This commit is contained in:
TheArchitectit 2026-06-15 08:55:19 -05:00
parent f6597f1935
commit 1b0d237de6
3 changed files with 16 additions and 11 deletions

View File

@ -7301,6 +7301,7 @@ fn run_tui_repl(mut cli: LiveCli) -> Result<(), Box<dyn std::error::Error>> {
let result = cli.run_turn_to(&trimmed, &mut buf, false);
drop(stdout);
app.resume()?;
app.set_turn_in_progress(false);
{
let messages = &cli.runtime.session().messages;

View File

@ -107,10 +107,14 @@ impl TuiApp {
/// Resume TUI after suspend.
pub fn resume(&mut self) -> Result<(), Box<dyn std::error::Error>> {
crossterm::execute!(
io::stdout(),
crossterm::terminal::EnterAlternateScreen,
Clear(ClearType::All),
crossterm::cursor::Hide
)?;
enable_raw_mode()?;
let _ = crossterm::execute!(io::stdout(), Clear(ClearType::All), crossterm::cursor::MoveTo(0, 0));
let _ = io::stdout().flush();
self.terminal.hide_cursor()?;
self.terminal.clear()?;
Ok(())
}
@ -226,6 +230,10 @@ impl TuiApp {
self.keymap.set_preset(preset);
}
pub fn set_turn_in_progress(&mut self, in_progress: bool) {
self.input_bar.set_turn_in_progress(in_progress);
}
pub fn key_preset_name(&self) -> &'static str {
match self.keymap.preset() {
KeyPreset::Emacs => "Emacs",
@ -284,6 +292,7 @@ impl TuiApp {
match outcome {
InputOutcome::Submit(text) => {
self.input_bar.push_history(&text);
self.set_turn_in_progress(true);
Ok(TuiReadOutcome::Submit(text))
}
InputOutcome::Cancel => Ok(TuiReadOutcome::Cancel),

View File

@ -105,6 +105,7 @@ impl InputBar {
pub fn set_turn_in_progress(&mut self, in_progress: bool) {
self.turn_in_progress = in_progress;
self.dirty = true;
}
/// Push text to history.
@ -293,23 +294,17 @@ impl InputBar {
width: area.width.min(40),
height: (matches.len() as u16 + 2).min(10),
};
frame.render_widget(ratatui::widgets::Clear, popup);
frame.render_widget(list, popup);
}
}
impl Component for InputBar {
fn render(&self, area: Rect, frame: &mut Frame, theme: &TuiTheme) {
frame.render_widget(ratatui::widgets::Clear, area);
let widget = self.textarea.clone();
frame.render_widget(&widget, area);
// Render completions popup above the input area
if self.showing_completions {
// Note: can't call self.render_completions() from &self here
// because we need mutable access to check state but render is &self.
// Workaround: render completions in the draw_frame function directly.
// This is acceptable for Phase 1 — will be refined later.
let _ = (area, frame, theme);
}
self.render_completions(area, frame, theme);
}
fn is_dirty(&self) -> bool {