atomcode_tuix/modals/mod.rs
1// crates/atomcode-tuix/src/modals/mod.rs
2//
3// Modal overlay abstraction. Each of the three existing overlays
4// (`/model` picker, `/provider` wizard, `/resume` session picker)
5// implements `Modal`, and the event loop owns exactly one
6// `active_modal: Option<Box<dyn Modal>>`. Adding a fourth modal means
7// "new struct + new impl" — not another `Option<T>` field and another
8// `handle_X_key` fn and another branch in `handle_input`.
9//
10// Modal impl lives next to the struct definition (for now still in
11// `event_loop.rs`; Step 5 will move each modal to its own file under
12// `crates/atomcode-tuix/src/modals/`). This module only defines the
13// trait + action enum.
14
15use anyhow::Result;
16use crossterm::event::{KeyCode, KeyModifiers};
17
18use crate::event_loop::{Buffer, LoopCtx};
19use crate::render::Renderer;
20use crate::state::UiState;
21
22pub mod dir_picker;
23pub mod issue_wizard;
24pub mod language_picker;
25pub mod model_picker;
26pub mod onboarding_wizard;
27mod qr;
28pub mod provider_wizard;
29pub mod session_picker;
30pub use dir_picker::DirPicker;
31pub use issue_wizard::IssueWizard;
32pub use language_picker::LanguagePicker;
33pub use model_picker::ModelPicker;
34pub use onboarding_wizard::OnboardingWizard;
35pub use provider_wizard::ProviderWizard;
36pub use session_picker::SessionPicker;
37
38/// Result of a modal consuming one key event.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum ModalAction {
41 /// Modal stays active; keep dispatching keys to it next time.
42 Continue,
43 /// Modal finished (user hit Esc, or the submit side-effect is
44 /// done). Caller must drop `active_modal` and redraw idle.
45 Close,
46}
47
48/// A modal overlay: takes over key handling until it returns
49/// `ModalAction::Close`. The implementation owns its own state (the
50/// selected index, the wizard step, the filter query, etc.) and is
51/// responsible for painting itself whenever its visible state changes
52/// — the event loop only calls `draw` once at open time and once more
53/// after `Close` to restore the idle prompt.
54pub trait Modal: Send {
55 /// Process one keystroke. Must either fully handle it (including
56 /// any re-paint the modal wants) or report that the modal is now
57 /// done so the caller can tear it down.
58 fn handle_key(
59 &mut self,
60 code: KeyCode,
61 mods: KeyModifiers,
62 buf: &mut Buffer,
63 state: &mut UiState,
64 ctx: &mut LoopCtx,
65 renderer: &mut dyn Renderer,
66 ) -> Result<ModalAction>;
67
68 /// Paint the modal against the current terminal state. Called once
69 /// when the modal is installed into `active_modal`; `handle_key`
70 /// is expected to handle subsequent repaints after each key.
71 fn draw(&self, buf: &Buffer, state: &UiState, ctx: &LoopCtx, renderer: &mut dyn Renderer);
72
73 /// Handle a bracketed-paste payload while the modal is active.
74 /// Default: append the text to `buf` (so text-input wizard steps
75 /// naturally accept URL / API-key paste) and redraw. Modals that
76 /// only present pickers (no text input) can leave the default —
77 /// buf updates are harmless when the modal isn't displaying it.
78 fn handle_paste(
79 &mut self,
80 text: &str,
81 buf: &mut Buffer,
82 state: &mut UiState,
83 ctx: &mut LoopCtx,
84 renderer: &mut dyn Renderer,
85 ) -> Result<ModalAction> {
86 buf.insert_paste(text.to_string());
87 self.draw(buf, state, ctx, renderer);
88 Ok(ModalAction::Continue)
89 }
90}