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:
parent
f6597f1935
commit
1b0d237de6
|
|
@ -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);
|
let result = cli.run_turn_to(&trimmed, &mut buf, false);
|
||||||
drop(stdout);
|
drop(stdout);
|
||||||
app.resume()?;
|
app.resume()?;
|
||||||
|
app.set_turn_in_progress(false);
|
||||||
|
|
||||||
{
|
{
|
||||||
let messages = &cli.runtime.session().messages;
|
let messages = &cli.runtime.session().messages;
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,14 @@ impl TuiApp {
|
||||||
|
|
||||||
/// Resume TUI after suspend.
|
/// Resume TUI after suspend.
|
||||||
pub fn resume(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
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()?;
|
enable_raw_mode()?;
|
||||||
let _ = crossterm::execute!(io::stdout(), Clear(ClearType::All), crossterm::cursor::MoveTo(0, 0));
|
|
||||||
let _ = io::stdout().flush();
|
let _ = io::stdout().flush();
|
||||||
self.terminal.hide_cursor()?;
|
|
||||||
self.terminal.clear()?;
|
self.terminal.clear()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -226,6 +230,10 @@ impl TuiApp {
|
||||||
self.keymap.set_preset(preset);
|
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 {
|
pub fn key_preset_name(&self) -> &'static str {
|
||||||
match self.keymap.preset() {
|
match self.keymap.preset() {
|
||||||
KeyPreset::Emacs => "Emacs",
|
KeyPreset::Emacs => "Emacs",
|
||||||
|
|
@ -284,6 +292,7 @@ impl TuiApp {
|
||||||
match outcome {
|
match outcome {
|
||||||
InputOutcome::Submit(text) => {
|
InputOutcome::Submit(text) => {
|
||||||
self.input_bar.push_history(&text);
|
self.input_bar.push_history(&text);
|
||||||
|
self.set_turn_in_progress(true);
|
||||||
Ok(TuiReadOutcome::Submit(text))
|
Ok(TuiReadOutcome::Submit(text))
|
||||||
}
|
}
|
||||||
InputOutcome::Cancel => Ok(TuiReadOutcome::Cancel),
|
InputOutcome::Cancel => Ok(TuiReadOutcome::Cancel),
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ impl InputBar {
|
||||||
|
|
||||||
pub fn set_turn_in_progress(&mut self, in_progress: bool) {
|
pub fn set_turn_in_progress(&mut self, in_progress: bool) {
|
||||||
self.turn_in_progress = in_progress;
|
self.turn_in_progress = in_progress;
|
||||||
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push text to history.
|
/// Push text to history.
|
||||||
|
|
@ -293,23 +294,17 @@ impl InputBar {
|
||||||
width: area.width.min(40),
|
width: area.width.min(40),
|
||||||
height: (matches.len() as u16 + 2).min(10),
|
height: (matches.len() as u16 + 2).min(10),
|
||||||
};
|
};
|
||||||
|
frame.render_widget(ratatui::widgets::Clear, popup);
|
||||||
frame.render_widget(list, popup);
|
frame.render_widget(list, popup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for InputBar {
|
impl Component for InputBar {
|
||||||
fn render(&self, area: Rect, frame: &mut Frame, theme: &TuiTheme) {
|
fn render(&self, area: Rect, frame: &mut Frame, theme: &TuiTheme) {
|
||||||
|
frame.render_widget(ratatui::widgets::Clear, area);
|
||||||
let widget = self.textarea.clone();
|
let widget = self.textarea.clone();
|
||||||
frame.render_widget(&widget, area);
|
frame.render_widget(&widget, area);
|
||||||
|
self.render_completions(area, frame, theme);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dirty(&self) -> bool {
|
fn is_dirty(&self) -> bool {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue