Skip to main content

flake_edit/tui/
run.rs

1//! TUI run loop for executing interactive workflows.
2
3use std::io;
4
5use crossterm::event::{self, Event, KeyEventKind};
6use ratatui::widgets::Widget;
7
8use super::app::{App, AppResult, UpdateResult};
9use super::backend::InlineTerminal;
10
11/// Run the app to completion, returning the result based on workflow type.
12///
13/// Returns `None` if the user cancelled, or `Some(AppResult)` with the
14/// appropriate result type for the workflow.
15pub fn run(mut app: App) -> io::Result<Option<AppResult>> {
16    let height = app.terminal_height();
17    let mut term = InlineTerminal::new(height)?;
18
19    loop {
20        // Resize terminal if needed (e.g., when transitioning to Confirm screen)
21        let new_height = app.terminal_height();
22        if new_height != term.height() {
23            tracing::trace!(old_height = term.height(), new_height, "resize viewport");
24            term.resize(new_height)?;
25        }
26
27        term.terminal().draw(|frame| {
28            let area = frame.area();
29            (&app).render(area, frame.buffer_mut());
30            if let Some((x, y)) = app.cursor_position(area) {
31                frame.set_cursor_position((x, y));
32            }
33        })?;
34
35        if let Event::Key(key) = event::read()? {
36            if key.kind != KeyEventKind::Press {
37                tracing::trace!(?key, "ignored non-press key event");
38                continue;
39            }
40            let result = app.update(key);
41            tracing::trace!(?key, ?result, "handled key event");
42            match result {
43                UpdateResult::Continue => {}
44                UpdateResult::Done => return Ok(app.extract_result()),
45                UpdateResult::Cancelled => return Ok(None),
46            }
47        }
48    }
49}