aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-ai/src/tui/view/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/atuin-ai/src/tui/view/mod.rs')
-rw-r--r--crates/atuin-ai/src/tui/view/mod.rs188
1 files changed, 67 insertions, 121 deletions
diff --git a/crates/atuin-ai/src/tui/view/mod.rs b/crates/atuin-ai/src/tui/view/mod.rs
index a1b32518..0cd51dfa 100644
--- a/crates/atuin-ai/src/tui/view/mod.rs
+++ b/crates/atuin-ai/src/tui/view/mod.rs
@@ -1,8 +1,7 @@
//! View function that builds the eye-declare element tree from app state.
use eye_declare::{
- Column, Component, Elements, HStack, Line, Span, Spinner, TextBlock, VStack, WidthConstraint,
- element, impl_slot_children,
+ Cells, Column, Elements, HStack, Span, Spinner, Text, View, WidthConstraint, element,
};
use ratatui_core::style::{Color, Modifier, Style};
@@ -13,40 +12,6 @@ use super::state::{AppMode, AppState};
mod turn;
-#[derive(Default)]
-struct Padding {
- top: u16,
- left: u16,
- right: u16,
- bottom: u16,
-}
-
-impl Component for Padding {
- type State = ();
-
- fn content_inset(&self, _state: &Self::State) -> eye_declare::Insets {
- eye_declare::Insets::ZERO
- .left(self.left)
- .right(self.right)
- .top(self.top)
- .bottom(self.bottom)
- }
-
- fn desired_height(&self, _width: u16, _state: &Self::State) -> u16 {
- 0
- }
-
- fn render(
- &self,
- _area: ratatui::layout::Rect,
- _buf: &mut ratatui::buffer::Buffer,
- _state: &(),
- ) {
- }
-}
-
-impl_slot_children!(Padding);
-
/// Build the element tree from current state.
///
/// Layout (top to bottom):
@@ -68,7 +33,7 @@ pub fn ai_view(state: &AppState) -> Elements {
element! {
AtuinAi(
- mode: state.mode.clone(),
+ mode: state.mode,
has_command: state.has_any_command(),
is_input_blank: state.is_input_blank,
pending_confirmation: state.confirmation_pending,
@@ -88,22 +53,24 @@ pub fn ai_view(state: &AppState) -> Elements {
})
#(if !state.is_exiting() {
- TextBlock { Line { Span(text: "") } }
- InputBox(
- key: "input",
- title: "Generate a command or ask a question",
- title_right: "Atuin AI",
- footer: state.footer_text(),
- active: state.mode == AppMode::Input && !state.confirmation_pending,
- )
+ View(key: "input-box", padding_top: Cells::from(1)) {
+ InputBox(
+ key: "input",
+ title: "Generate a command or ask a question",
+ title_right: "Atuin AI",
+ footer: state.footer_text(),
+ active: state.mode == AppMode::Input && !state.confirmation_pending,
+ )
- #(if state.is_input_blank && state.has_any_command() && state.mode == AppMode::Input {
- #(if state.confirmation_pending {
- TextBlock { Line { Span(text: "[Enter] Confirm dangerous command [Esc] Cancel", style: Style::default().fg(Color::Gray)) } }
- } else {
- TextBlock { Line { Span(text: "[Enter] Execute suggested command [Tab] Insert Command", style: Style::default().fg(Color::Gray)) } }
+ #(if state.is_input_blank && state.has_any_command() && state.mode == AppMode::Input {
+ #(if state.confirmation_pending {
+ Text { Span(text: "[Enter] Confirm dangerous command [Esc] Cancel", style: Style::default().fg(Color::Gray)) }
+ } else {
+ Text { Span(text: "[Enter] Execute suggested command [Tab] Insert Command", style: Style::default().fg(Color::Gray)) }
+ })
})
- })
+
+ }
})
}
}
@@ -114,25 +81,20 @@ fn user_turn_view(events: &[turn::UiEvent], first_turn: bool) -> Elements {
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD);
+ let padding = if first_turn { 0 } else { 1 };
+
element! {
- VStack {
- TextBlock {
- #(if !first_turn {
- Line { Span() }
- })
- Line {
- Span(text: "You", style: label_style)
- }
+ View(padding_top: Cells::from(padding)) {
+ Text {
+ Span(text: "You", style: label_style)
}
#(for event in events {
#(match event {
turn::UiEvent::Text { content } => {
element! {
- Padding(left: 2u16) {
- TextBlock {
- Line {
- Span(text: content, style: Style::default())
- }
+ View(padding_left: Cells::from(2)) {
+ Text {
+ Span(text: content, style: Style::default())
}
}
}
@@ -150,7 +112,7 @@ fn agent_turn_view(events: &[turn::UiEvent], busy: bool) -> Elements {
.add_modifier(Modifier::BOLD);
element! {
- VStack {
+ View {
Spinner(
label: "Atuin AI",
label_style: label_style,
@@ -163,7 +125,7 @@ fn agent_turn_view(events: &[turn::UiEvent], busy: bool) -> Elements {
#(match event {
turn::UiEvent::Text { content } => {
element! {
- Padding(left: 2u16) {
+ View(padding_left: Cells::from(2)) {
Markdown(source: content)
}
}
@@ -183,9 +145,9 @@ fn agent_turn_view(events: &[turn::UiEvent], busy: bool) -> Elements {
fn out_of_band_turn_view(events: &[turn::UiEvent]) -> Elements {
element! {
- VStack {
- TextBlock {
- Line { Span(text: "System", style: Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD)) }
+ View {
+ Text {
+ Span(text: "System", style: Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD))
}
#(for event in events {
#(match event {
@@ -201,12 +163,10 @@ fn out_of_band_turn_view(events: &[turn::UiEvent]) -> Elements {
fn out_of_band_output_view(details: &turn::OutOfBandOutputDetails) -> Elements {
element! {
- Padding(left: 2u16) {
+ View(padding_left: Cells::from(2)) {
#(if details.command.is_some() {
- TextBlock {
- Line {
- Span(text: details.command.as_ref().unwrap(), style: Style::default().fg(Color::Blue))
- }
+ Text {
+ Span(text: details.command.as_ref().unwrap(), style: Style::default().fg(Color::Blue))
}
})
Markdown(source: details.content.clone())
@@ -254,82 +214,68 @@ fn suggested_command_view(details: &turn::SuggestedCommandDetails) -> Elements {
let confidence_notes = details.confidence_level.notes();
element! {
- VStack {
- TextBlock {
- #(if !details.first_event_in_turn {
- Line { Span() }
- })
- Line {
- Span(text: " Suggested command:", style: Style::default().fg(Color::Cyan))
- }
+ View {
+ #(if !details.first_event_in_turn {
+ Text { Span(text: "") }
+ })
+ Text {
+ Span(text: " Suggested command:", style: Style::default().fg(Color::Cyan))
}
HStack {
- Column(width: WidthConstraint::Fixed(2)) {
- TextBlock {
- Line {
- #(if is_dangerous || low_confidence {
- Span(text: "! ", style: Style::default().fg(Color::Yellow))
- } else {
- Span(text: "$ ", style: Style::default().fg(Color::Blue))
- })
- }
+ View(width: WidthConstraint::Fixed(2)) {
+ Text {
+ #(if is_dangerous || low_confidence {
+ Span(text: "! ", style: Style::default().fg(Color::Yellow))
+ } else {
+ Span(text: "$ ", style: Style::default().fg(Color::Blue))
+ })
}
}
Column {
- TextBlock {
- Line {
- Span(text: &details.command, style: Style::default().fg(Color::Green))
- }
+ Text {
+ Span(text: &details.command, style: Style::default().fg(Color::Green))
}
}
}
#(if is_dangerous {
- Padding(left: 2u16) {
- TextBlock {
- Line {
- Span(text: "Danger: ", style: danger_style)
- Span(text: danger_text, style: danger_style.add_modifier(Modifier::BOLD))
- }
+ View(padding_left: Cells::from(2)) {
+ Text {
+ Span(text: "Danger: ", style: danger_style)
+ Span(text: danger_text, style: danger_style.add_modifier(Modifier::BOLD))
}
}
})
#(if is_dangerous && danger_notes.is_some() {
- Padding(left: 2u16) {
+ View(padding_left: Cells::from(2)) {
HStack {
- Column(width: WidthConstraint::Fixed(2)) {
- TextBlock {
- Line {
- Span(text: "└")
- }
+ View(width: WidthConstraint::Fixed(2)) {
+ Text {
+ Span(text: "└")
}
}
- Column(width: WidthConstraint::Fill) {
+ View(width: WidthConstraint::Fill) {
Markdown(source: danger_notes.unwrap())
}
}
}
})
#(if low_confidence {
- Padding(left: 2u16) {
- TextBlock {
- Line {
- Span(text: "Confidence: ", style: Style::default().fg(Color::Blue))
- Span(text: confidence_level, style: Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD))
- }
+ View(padding_left: Cells::from(2)) {
+ Text {
+ Span(text: "Confidence: ", style: Style::default().fg(Color::Blue))
+ Span(text: confidence_level, style: Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD))
}
}
})
#(if low_confidence && confidence_notes.is_some() {
- Padding(left: 2u16) {
+ View(padding_left: Cells::from(2)) {
HStack {
- Column(width: WidthConstraint::Fixed(2)) {
- TextBlock {
- Line {
- Span(text: "└")
- }
+ View(width: WidthConstraint::Fixed(2)) {
+ Text {
+ Span(text: "└")
}
}
- Column(width: WidthConstraint::Fill) {
+ View(width: WidthConstraint::Fill) {
Markdown(source: confidence_notes.unwrap())
}
}