aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-ai/src/tui/components
diff options
context:
space:
mode:
authorMichelle Tilley <michelle@michelletilley.net>2026-04-21 13:07:27 -0700
committerGitHub <noreply@github.com>2026-04-21 13:07:27 -0700
commit2f702ad446fcd6a261a3bea0ab2807d70eca43e2 (patch)
tree4cfa6276257cefbe73f7fa46a74026170aaf8435 /crates/atuin-ai/src/tui/components
parentdocs: document show_numeric_shortcuts (#3433) (diff)
downloadatuin-2f702ad446fcd6a261a3bea0ab2807d70eca43e2.zip
refactor: Replace ad-hoc dispatch with FSM + driver architecture (#3434)
Replaces the tangled dispatch handler system (`tui/dispatch.rs`, `tui/state.rs`) with a pure finite state machine + driver architecture. The FSM handles all state transitions as explicit `(State, Event) → (NewState, Effects)` mappings. The driver executes IO effects and bridges the TUI to the FSM.
Diffstat (limited to 'crates/atuin-ai/src/tui/components')
-rw-r--r--crates/atuin-ai/src/tui/components/atuin_ai.rs7
-rw-r--r--crates/atuin-ai/src/tui/components/input_box.rs12
-rw-r--r--crates/atuin-ai/src/tui/components/select.rs7
3 files changed, 14 insertions, 12 deletions
diff --git a/crates/atuin-ai/src/tui/components/atuin_ai.rs b/crates/atuin-ai/src/tui/components/atuin_ai.rs
index c7227fbd..31dff1c3 100644
--- a/crates/atuin-ai/src/tui/components/atuin_ai.rs
+++ b/crates/atuin-ai/src/tui/components/atuin_ai.rs
@@ -5,11 +5,10 @@
//! Tab) are handled in the bubble phase so child components like the
//! permission Select can consume them first.
-use std::sync::mpsc;
-
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use eye_declare::{Elements, EventResult, Hooks, component, props};
+use crate::commands::inline::DriverEventSender;
use crate::tui::events::AiTuiEvent;
use crate::tui::state::AppMode;
@@ -28,7 +27,7 @@ pub(crate) struct AtuinAi {
#[derive(Default)]
pub(crate) struct AtuinAiState {
- tx: Option<mpsc::Sender<AiTuiEvent>>,
+ tx: Option<DriverEventSender>,
}
#[component(props = AtuinAi, state = AtuinAiState, children = Elements)]
@@ -38,7 +37,7 @@ fn atuin_ai(
hooks: &mut Hooks<AtuinAi, AtuinAiState>,
children: Elements,
) -> Elements {
- hooks.use_context::<mpsc::Sender<AiTuiEvent>>(|tx, _, state| {
+ hooks.use_context::<DriverEventSender>(|tx, _, state| {
state.tx = tx.cloned();
});
diff --git a/crates/atuin-ai/src/tui/components/input_box.rs b/crates/atuin-ai/src/tui/components/input_box.rs
index 6e041418..6b81322c 100644
--- a/crates/atuin-ai/src/tui/components/input_box.rs
+++ b/crates/atuin-ai/src/tui/components/input_box.rs
@@ -6,7 +6,7 @@
//!
//! On Enter, sends `AiTuiEvent::SubmitInput` via the context-provided channel.
-use std::sync::{Arc, Mutex, mpsc};
+use std::sync::{Arc, Mutex};
use crossterm::event::KeyModifiers;
use eye_declare::{Canvas, Elements, EventResult, Hooks, component, element, props};
@@ -19,6 +19,7 @@ use ratatui_core::{
};
use tui_textarea::TextArea;
+use crate::commands::inline::DriverEventSender;
use crate::tui::{events::AiTuiEvent, slash::SlashCommandSearchResult};
/// A bordered text input box backed by tui-textarea.
@@ -41,7 +42,7 @@ pub(crate) struct InputBox {
pub(crate) struct InputBoxState {
textarea: Arc<Mutex<TextArea<'static>>>,
- tx: Option<mpsc::Sender<AiTuiEvent>>,
+ tx: Option<DriverEventSender>,
}
impl Default for InputBoxState {
@@ -97,10 +98,13 @@ fn input_box(
state: &InputBoxState,
hooks: &mut Hooks<InputBox, InputBoxState>,
) -> Elements {
- hooks.use_focusable(props.active);
+ // Always focusable so focus isn't lost when the permission Select is
+ // removed from the tree. The `active` prop controls visual state and
+ // whether keystrokes are processed, not focusability.
+ hooks.use_focusable(true);
hooks.use_autofocus();
- hooks.use_context::<mpsc::Sender<AiTuiEvent>>(|tx, _, state| {
+ hooks.use_context::<DriverEventSender>(|tx, _, state| {
state.tx = tx.cloned();
});
diff --git a/crates/atuin-ai/src/tui/components/select.rs b/crates/atuin-ai/src/tui/components/select.rs
index 5abbe655..771d7830 100644
--- a/crates/atuin-ai/src/tui/components/select.rs
+++ b/crates/atuin-ai/src/tui/components/select.rs
@@ -1,10 +1,9 @@
-use std::sync::mpsc;
-
use crossterm::event::KeyCode;
use eye_declare::{Elements, EventResult, Hooks, Span, Text, View, component, element, props};
use ratatui::style::Style;
use typed_builder::TypedBuilder;
+use crate::commands::inline::DriverEventSender;
use crate::tui::events::AiTuiEvent;
type OnSelectFn = Box<dyn Fn(&SelectOption) -> Option<AiTuiEvent> + Send + Sync + 'static>;
@@ -24,7 +23,7 @@ pub(crate) struct SelectOption {
#[derive(Default)]
pub(crate) struct PermissionSelectorState {
selected_option: usize,
- tx: Option<mpsc::Sender<AiTuiEvent>>,
+ tx: Option<DriverEventSender>,
}
#[props]
@@ -42,7 +41,7 @@ pub(crate) fn permission_selector(
hooks.use_focusable(true);
hooks.use_autofocus();
- hooks.use_context::<mpsc::Sender<AiTuiEvent>>(|tx, _, state| {
+ hooks.use_context::<DriverEventSender>(|tx, _, state| {
state.tx = tx.cloned();
});