notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit d4681801e8fd105331110249fbaa121bd1854da5
parent 9ec5f1efa2715b5792c24c05c26c7c29e27cef5c
Author: William Casarin <jb55@jb55.com>
Date:   Mon, 21 Apr 2025 17:10:03 -0700

dave: add new chat button

Diffstat:
Mcrates/notedeck_dave/src/lib.rs | 8++++++++
Mcrates/notedeck_dave/src/ui/dave.rs | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -157,6 +157,11 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr DaveUi::new(&self.chat, &mut self.input).ui(app_ctx, ui) } + fn handle_new_chat(&mut self) { + self.chat = vec![]; + self.input.clear(); + } + /// Handle a user send action triggered by the ui fn handle_user_send(&mut self, app_ctx: &AppContext, ui: &egui::Ui) { self.chat.push(Message::User(self.input.clone())); @@ -296,6 +301,9 @@ impl notedeck::App for Dave { let should_send = self.process_events(ctx); if let Some(action) = self.ui(ctx, ui).action { match action { + DaveAction::NewChat => { + self.handle_new_chat(); + } DaveAction::Send => { self.handle_user_send(ctx, ui); } diff --git a/crates/notedeck_dave/src/ui/dave.rs b/crates/notedeck_dave/src/ui/dave.rs @@ -21,13 +21,23 @@ pub struct DaveResponse { } impl DaveResponse { - /// Generate a send response to the controller - fn send() -> Self { + fn new(action: DaveAction) -> Self { + DaveResponse { + action: Some(action), + } + } + + fn or(self, r: DaveResponse) -> DaveResponse { DaveResponse { - action: Some(DaveAction::Send), + action: self.action.or(r.action), } } + /// Generate a send response to the controller + fn send() -> Self { + Self::new(DaveAction::Send) + } + fn none() -> Self { DaveResponse::default() } @@ -40,6 +50,7 @@ impl DaveResponse { pub enum DaveAction { /// The action generated when the user sends a message to dave Send, + NewChat, } impl<'a> DaveUi<'a> { @@ -67,7 +78,22 @@ impl<'a> DaveUi<'a> { /// The main render function. Call this to render Dave pub fn ui(&mut self, app_ctx: &mut AppContext, ui: &mut egui::Ui) -> DaveResponse { + let mut action: Option<DaveAction> = None; // Scroll area for chat messages + let new_resp = { + let mut rect = ui.available_rect_before_wrap(); + rect = rect.translate(egui::vec2(20.0, 20.0)); + rect.set_width(32.0); + rect.set_height(32.0); + ui.put(rect, new_chat_button()) + }; + + if new_resp.clicked() { + action = Some(DaveAction::NewChat); + } else if new_resp.hovered() { + notedeck_ui::show_pointer(ui); + } + egui::Frame::NONE .show(ui, |ui| { ui.with_layout(Layout::bottom_up(Align::Min), |ui| { @@ -82,7 +108,6 @@ impl<'a> DaveUi<'a> { }) .inner_margin(egui::Margin::same(8)) .fill(ui.visuals().extreme_bg_color) - //.stroke(stroke) .corner_radius(12.0) .show(ui, |ui| self.inputbox(ui)) .inner; @@ -103,6 +128,7 @@ impl<'a> DaveUi<'a> { .inner }) .inner + .or(DaveResponse { action }) } /// Render a chat message (user, assistant, tool call/response, etc) @@ -251,3 +277,29 @@ impl<'a> DaveUi<'a> { }); } } + +fn new_chat_button() -> impl egui::Widget { + move |ui: &mut egui::Ui| { + let img_size = 24.0; + let max_size = 32.0; + + let img_data = egui::include_image!("../../../../assets/icons/newmessage_64.png"); + let img = egui::Image::new(img_data).max_width(img_size); + + let helper = notedeck_ui::anim::AnimationHelper::new( + ui, + "new-chat-button", + egui::vec2(max_size, max_size), + ); + + let cur_img_size = helper.scale_1d_pos(img_size); + img.paint_at( + ui, + helper + .get_animation_rect() + .shrink((max_size - cur_img_size) / 2.0), + ); + + helper.take_animation_response() + } +}