- 1
//! Pinned bottom command composer and floating Quick Action Palette overlay. - 2
- 3
use ratatui::buffer::Buffer; - 4
use ratatui::layout::{Constraint, Direction, Layout, Rect}; - 5
use ratatui::style::Modifier; - 6
use ratatui::text::{Line, Span}; - 7
use ratatui::widgets::{Block, Borders, Clear, Paragraph, Widget}; - 8
- 9
use crate::repl::ReplComposer; - 10
use crate::theme::{Symbols, Theme}; - 11
- 12
pub struct ComposerView<'a> { - 13
pub composer: &'a ReplComposer, - 14
pub theme: &'a Theme, - 15
pub connected: bool, - 16
pub error: Option<String>, - 17
} - 18
- 19
impl<'a> Widget for ComposerView<'a> { - 20
fn render(self, area: Rect, buf: &mut Buffer) { - 21
let chunks = Layout::default() - 22
.direction(Direction::Vertical) - 23
.constraints([Constraint::Length(3), Constraint::Length(1)]) - 24
.split(area); - 25
- 26
// 1. Input Box - 27
let prompt_label = if self.connected { - 28
format!(" {} ", Symbols::PROMPT_CHEVRON) - 29
} else { - 30
format!(" {} ", Symbols::STATUS_ACTIVE) - 31
}; - 32
let prompt_span = Span::styled( - 33
prompt_label, - 34
self.theme.style_accent().add_modifier(Modifier::BOLD), - 35
); - 36
- 37
let input_text = &self.composer.buffer; - 38
let input_span = if self.connected { - 39
Span::styled(input_text, self.theme.style_card()) - 40
} else { - 41
Span::styled( - 42
input_text, - 43
self.theme.style_card().add_modifier(Modifier::DIM), - 44
) - 45
}; - 46
let cursor_span = Span::styled("█", self.theme.style_accent()); - 47
- 48
let mut line_spans = vec![prompt_span, input_span, cursor_span]; - 49
if let Some(err) = &self.error { - 50
line_spans.push(Span::styled( - 51
format!(" [ERR: {err}]"), - 52
self.theme.style_danger(), - 53
)); - 54
} - 55
let line = Line::from(line_spans); - 56
- 57
let block = Block::default() - 58
.borders(Borders::ALL) - 59
.border_style(self.theme.style_border_focus()) - 60
.title(" Command composer ") - 61
.title_style(self.theme.style_accent()); - 62
- 63
let input_para = Paragraph::new(line).block(block); - 64
input_para.render(chunks[0], buf); - 65
- 66
// 2. Shortcut Hint Strip - 67
let shortcuts = [ - 68
("[Enter]", "Send"), - 69
("[Shift+Enter]", "Line"), - 70
("[/]", "Commands"), - 71
("[Tab]", "Deck Focus"), - 72
("[F2]", "Theme"), - 73
("[Ctrl+D]", "Detach"), - 74
]; - 75
- 76
let mut hint_spans = Vec::new(); - 77
hint_spans.push(Span::raw(" ")); - 78
for (key, action) in shortcuts { - 79
hint_spans.push(Span::styled( - 80
key, - 81
self.theme.style_card().add_modifier(Modifier::BOLD), - 82
)); - 83
hint_spans.push(Span::styled( - 84
format!(" {action} "), - 85
self.theme.style_card(), - 86
)); - 87
} - 88
- 89
let hint_para = Paragraph::new(Line::from(hint_spans)); - 90
hint_para.render(chunks[1], buf); - 91
- 92
// 3. Quick Action Palette Overlay (if open) - 93
if self.composer.slash_palette_open { - 94
let matching = self.composer.matching_slash_commands(); - 95
let pal_h = (matching.len() as u16 + 2).min(8); - 96
let pal_w = 48.min(area.width.saturating_sub(4)); - 97
let pal_area = Rect { - 98
x: area.x + 4, - 99
y: chunks[0].y.saturating_sub(pal_h), - 100
width: pal_w, - 101
height: pal_h, - 102
}; - 103
- 104
Clear.render(pal_area, buf); - 105
- 106
let mut pal_lines = Vec::new(); - 107
for (idx, cmd) in matching.iter().enumerate() { - 108
let is_selected = idx == self.composer.selected_slash_cmd; - 109
let prefix = if is_selected { " > " } else { " " }; - 110
let style = if is_selected { - 111
self.theme.style_tab_active() - 112
} else { - 113
self.theme.style_card() - 114
}; - 115
- 116
pal_lines.push(Line::from(vec![ - 117
Span::styled(format!("{prefix}{:<10}", cmd.name), style), - 118
Span::styled(format!(" {}", cmd.description), self.theme.style_card()), - 119
])); - 120
} - 121
- 122
let pal_block = Block::default() - 123
.borders(Borders::ALL) - 124
.border_style(self.theme.style_border_focus()) - 125
.title(" Quick Action Palette (Enter/Tab) ") - 126
.title_style(self.theme.style_accent()); - 127
- 128
let pal_para = Paragraph::new(pal_lines).block(pal_block); - 129
pal_para.render(pal_area, buf); - 130
} - 131
} - 132
} - 133
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.