main: implement the ability to replay eva utterances via list selection
This commit is contained in:
+53
-3
@@ -81,6 +81,13 @@ struct App {
|
||||
audio_level: f64,
|
||||
recording_audio: bool,
|
||||
audio_control_sink: tokio::sync::mpsc::Sender<AudioRecordRequest>,
|
||||
focus_state: FocusState
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FocusState {
|
||||
Conversation,
|
||||
UserInput
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -89,6 +96,7 @@ impl App {
|
||||
scene: Scene::default(),
|
||||
next_reply_options: Vec::new(),
|
||||
reply_state: ListState::default(),
|
||||
conversation_state: ListState::default(),
|
||||
user_input: Input::default(),
|
||||
end_time: Utc::now() + Duration::hours(2),
|
||||
throbber_state: ThrobberState::default(),
|
||||
@@ -97,10 +105,11 @@ impl App {
|
||||
audio_level: -60.,
|
||||
recording_audio: false,
|
||||
audio_control_sink,
|
||||
focus_state: FocusState::UserInput
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_conversation(&self, frame: &mut Frame, area: Rect) {
|
||||
fn draw_conversation(&mut self, frame: &mut Frame, area: Rect) {
|
||||
let items: Vec<Line> = self.scene.conversation.iter().rev().map(|entry| {
|
||||
match entry {
|
||||
ConversationEntry::User(text) => Line::from_iter([Span::from("Argee: ").style(ratatui::style::Color::Magenta), Span::from(text)]),
|
||||
@@ -110,7 +119,17 @@ impl App {
|
||||
ConversationEntry::SystemMessage(text) => Line::from_iter([text]).style(ratatui::style::Color::DarkGray)
|
||||
}
|
||||
}).collect();
|
||||
frame.render_widget(List::new(items).block(Block::bordered().border_style(style::Color::LightYellow).title("Conversation")).direction(ListDirection::BottomToTop), area);
|
||||
// FIXME: We need to somehow make long list items wrap. https://github.com/ratatui/ratatui/issues/128#issuecomment-1613918499
|
||||
// TODO: Would be nice to be able to scroll a longer conversation with the scroll wheel, or with page up/down
|
||||
frame.render_stateful_widget(
|
||||
List::new(items)
|
||||
.block(Block::bordered().border_style(style::Color::LightYellow).title("Conversation (Press Tab to select and read Eva's lines aloud)"))
|
||||
.direction(ListDirection::BottomToTop)
|
||||
.highlight_symbol("> ")
|
||||
.highlight_style(style::Style::new().bold().fg(style::Color::Cyan)),
|
||||
area,
|
||||
&mut self.conversation_state
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_options(&mut self, frame: &mut Frame, area: Rect) {
|
||||
@@ -261,7 +280,36 @@ impl App {
|
||||
|
||||
async fn on_event(&mut self, evt: event::Event) {
|
||||
if let Some(key) = evt.as_key_press_event() {
|
||||
match self.focus_state {
|
||||
FocusState::Conversation => {
|
||||
match key.code {
|
||||
KeyCode::Tab => {
|
||||
self.focus_state = FocusState::UserInput;
|
||||
self.conversation_state.select(None);
|
||||
},
|
||||
KeyCode::PageUp => self.conversation_state.scroll_down_by(5),
|
||||
KeyCode::PageDown => self.conversation_state.scroll_up_by(5),
|
||||
KeyCode::Home => self.conversation_state.select_last(),
|
||||
KeyCode::End => self.conversation_state.select_first(),
|
||||
KeyCode::Down => self.conversation_state.select_previous(),
|
||||
KeyCode::Up => self.conversation_state.select_next(),
|
||||
KeyCode::Enter => {
|
||||
let row_num = self.conversation_state.selected().unwrap();
|
||||
if let ConversationEntry::Eva(text) = &self.scene.conversation[self.scene.conversation.len() - 1 - row_num] {
|
||||
self.speak(text.clone().as_str());
|
||||
self.focus_state = FocusState::UserInput;
|
||||
self.conversation_state.select(None);
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
FocusState::UserInput => {
|
||||
match key.code {
|
||||
KeyCode::Tab => {
|
||||
self.focus_state = FocusState::Conversation;
|
||||
self.conversation_state.select_first();
|
||||
},
|
||||
KeyCode::Char('r') if key.modifiers.contains(KeyModifiers::CONTROL) => self.regenerate_responses(),
|
||||
KeyCode::Char('x') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
if self.recording_audio {
|
||||
@@ -275,7 +323,7 @@ impl App {
|
||||
},
|
||||
KeyCode::Down => self.reply_state.select_next(),
|
||||
KeyCode::Up => self.reply_state.select_previous(),
|
||||
KeyCode::Tab => {
|
||||
KeyCode::Enter if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
self.insert_selected_prompt();
|
||||
},
|
||||
KeyCode::Enter => {
|
||||
@@ -325,6 +373,8 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_bandcamp_artifact(&mut self, url: &str) {
|
||||
let body = reqwest::get(url).await.unwrap().text().await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user